首页 \ 问答 \ 大文件上的aio_write(aio_write on Large Files)

大文件上的aio_write(aio_write on Large Files)

我试图用aio_read(3)aio_write(3)以异步的方式模仿sendfile(2)的功能。

除了测试大型(> 150k)文件外,一切似乎都正常工作。

我有一个简单的struct io_request用于跟踪传输:

struct io_request {
    int status;
    struct aiocb *aiocbp;
    int sfd;
};

首先,我建立了aio_read()调用:

struct io_request * ioreq = malloc(sizeof(struct io_request));
ioreq->status = EINPROGRESS;
ioreq->sfd = sfd;

struct aiocb * aiocbreq = malloc(sizeof(struct aiocb));
memset(aiocbreq, 0, sizeof(struct aiocb));

ioreq->aiocbp = aiocbreq;

ioreq->aiocbp->aio_fildes = ffd;

if (ioreq->aiocbp->aio_fildes == -1) {
    perror("aio_fildes");
}

ioreq->aiocbp->aio_buf = malloc(st.st_size);
if (ioreq->aiocbp->aio_buf == NULL) {
    perror("aio_buf malloc");
}

ioreq->aiocbp->aio_nbytes = st.st_size;
ioreq->aiocbp->aio_reqprio = 0;
ioreq->aiocbp->aio_offset = 0;
ioreq->aiocbp->aio_sigevent.sigev_signo = IO_READ_SIGNAL;
ioreq->aiocbp->aio_sigevent.sigev_value.sival_ptr = ioreq;
if (aio_read(ioreq->aiocbp) == -1) {
    perror("aio_read");
}

然后在IO_READ_SIGNAL处理程序中捕获IO_READ_SIGNAL

static void
aio_read_handler(int sig, siginfo_t *si, void *ucontext)
{
    if (si->si_code == SI_ASYNCIO) {
        struct io_request *ioreq = si->si_value.sival_ptr;

        // Build the AIO write request
        struct aiocb aiocbreq;
        memset(&aiocbreq, 0, sizeof(struct aiocb));
        aiocbreq.aio_fildes = ioreq->sfd;
        aiocbreq.aio_buf = ioreq->aiocbp->aio_buf;
        aiocbreq.aio_nbytes = ioreq->aiocbp->aio_nbytes;
        aiocbreq.aio_sigevent.sigev_signo = IO_WRITE_SIGNAL;
        aiocbreq.aio_sigevent.sigev_value.sival_ptr = ioreq;

        if (aio_write((void *) &aiocbreq) == -1) {
            perror("aio_write");
        }
    }
}

我可以确认在处理程序内部,即使是大文件, ioreq->aiocbp->aio_buf的内容也是完整且完整的。

稍后,将在IO_WRITE_SIGNAL处理程序中捕获aio_write()

static void
aio_write_handler(int sig, siginfo_t *si, void *ucontext)
{
    if (si->si_code == SI_ASYNCIO) {
        struct io_request *ioreq = si->si_value.sival_ptr;

        ssize_t bytes_written = aio_return(ioreq->aiocbp);
        printf("Wrote %zu of %zu bytes\n", bytes_written, ioreq->aiocbp->aio_nbytes);
        //free(ioreq->aiocbp);
        //free(ioreq);

        if (aio_error(ioreq->aiocbp) != 0) {
            perror("aio_write_handler");
        }
    }
}

此时aio_write()应该已经完成​​。 我检查返回值并据此采取行动。 这两个调用都会报告已写入的相应字节数,并且在写入期间不会出现错误。

更大的应用程序是一个HTTP服务器。 我推测这个问题是由于远程客户端无法快速读取以跟上aio_write() 。 当我有一个sendfile()实现时,我不得不多次调用sendfile()来完成文件传输。

几个直接的问题:

  • 为什么aio_return()aio_error()不报告任何问题?
  • 我该如何解决这个问题?
  • 有没有办法缓冲aio_write() ? 我正在考虑n_bytes传递给aio_write() struct aiocbn_bytes ,并在aio_write()多次调用aio_write_handler()

谢谢你的帮助!


I'm trying to essentially mimic the functionality of sendfile(2) in an asynchronous fashion using aio_read(3) and aio_write(3).

Everything seems to be working fine, with the exception of testing large (> 150k) files.

I have a simple struct io_request I am using to keep track of the transfers:

struct io_request {
    int status;
    struct aiocb *aiocbp;
    int sfd;
};

First, I build the aio_read() call:

struct io_request * ioreq = malloc(sizeof(struct io_request));
ioreq->status = EINPROGRESS;
ioreq->sfd = sfd;

struct aiocb * aiocbreq = malloc(sizeof(struct aiocb));
memset(aiocbreq, 0, sizeof(struct aiocb));

ioreq->aiocbp = aiocbreq;

ioreq->aiocbp->aio_fildes = ffd;

if (ioreq->aiocbp->aio_fildes == -1) {
    perror("aio_fildes");
}

ioreq->aiocbp->aio_buf = malloc(st.st_size);
if (ioreq->aiocbp->aio_buf == NULL) {
    perror("aio_buf malloc");
}

ioreq->aiocbp->aio_nbytes = st.st_size;
ioreq->aiocbp->aio_reqprio = 0;
ioreq->aiocbp->aio_offset = 0;
ioreq->aiocbp->aio_sigevent.sigev_signo = IO_READ_SIGNAL;
ioreq->aiocbp->aio_sigevent.sigev_value.sival_ptr = ioreq;
if (aio_read(ioreq->aiocbp) == -1) {
    perror("aio_read");
}

Which then later is captured in a IO_READ_SIGNAL handler:

static void
aio_read_handler(int sig, siginfo_t *si, void *ucontext)
{
    if (si->si_code == SI_ASYNCIO) {
        struct io_request *ioreq = si->si_value.sival_ptr;

        // Build the AIO write request
        struct aiocb aiocbreq;
        memset(&aiocbreq, 0, sizeof(struct aiocb));
        aiocbreq.aio_fildes = ioreq->sfd;
        aiocbreq.aio_buf = ioreq->aiocbp->aio_buf;
        aiocbreq.aio_nbytes = ioreq->aiocbp->aio_nbytes;
        aiocbreq.aio_sigevent.sigev_signo = IO_WRITE_SIGNAL;
        aiocbreq.aio_sigevent.sigev_value.sival_ptr = ioreq;

        if (aio_write((void *) &aiocbreq) == -1) {
            perror("aio_write");
        }
    }
}

I can confirm that inside the handler, even for large files, the contents of ioreq->aiocbp->aio_buf is full and complete.

Later, the aio_write() is captured in a IO_WRITE_SIGNAL handler:

static void
aio_write_handler(int sig, siginfo_t *si, void *ucontext)
{
    if (si->si_code == SI_ASYNCIO) {
        struct io_request *ioreq = si->si_value.sival_ptr;

        ssize_t bytes_written = aio_return(ioreq->aiocbp);
        printf("Wrote %zu of %zu bytes\n", bytes_written, ioreq->aiocbp->aio_nbytes);
        //free(ioreq->aiocbp);
        //free(ioreq);

        if (aio_error(ioreq->aiocbp) != 0) {
            perror("aio_write_handler");
        }
    }
}

At this point aio_write() should have been completed. I check the return values and act accordingly. Both calls report the appropriate number of bytes have been written and no errors arose during the write.

The greater application is an HTTP server. I speculate that this problem arrises because the remote client cannot read fast enough to keep up with the aio_write(). When I had a sendfile() implementation of this, I had to call sendfile() multiple times to complete the file transfer.

Several direct questions:

  • Why does aio_return() and aio_error() not report any problems?
  • How can I fix this behavior?
  • Are there ways to buffer aio_write()? I was thinking of capping of n_bytes inside struct aiocb passed to aio_write(), and just calling aio_write() multiple times from inside aio_write_handler().

Thanks for your help!


原文:https://stackoverflow.com/questions/41046769
更新时间:2023-09-24 06:09

最满意答案

我们可以按值传递参数的原因完全相同 - 如果您需要副本,请按值获取。

我无法想象通过显式创建副本而不是为您执行循环而无法解决的任何问题。

我认为这是你遇到的主要问题。 循环不是“为你做”。 这明确要求副本。 什么比初始化非参考变量更明确?

这实际上只是一个普通的声明。 为什么,当它在其他地方都有效时,我们会让auto本身无效吗? 实际上,该声明的初始化由标准定义为:

auto x = *__begin;

其中__begin是赋予范围的第一个元素的迭代器的表达式(在本例中为v.begin() )。 这与C ++中的任何其他复制没有什么不同。 你会认为以下是一个常见的错误吗?

int x = some_other_int;

要么:

std::string str = some_other_string;

不,当我们想要一份副本时,我们会写一个这样的声明。

这是一个用例示例:

void modify_argument(X&);
void use(X);

// ...

std::vector<X> v = /* ... */;
for (auto x : v) {
  // We want to modify the copy of x, but not the original:
  modify_argument(x);
  use(x);
}

Exactly the same reason we can pass arguments by value - if you need a copy, take it by value.

I can't imagine anything that can not be solved by just creating a copy explicitly instead of the loop doing it for you.

I think this is the main issue you're having. The loop isn't "doing it for you". This is asking for a copy explicitly. What's more explicit than initialising a non-reference variable?

This is really just a normal declaration. Why, when it is valid everywhere else, would we make auto by itself be invalid here? In fact, the initialization of this declaration is defined by the standard as:

auto x = *__begin;

where __begin is the expression giving an iterator to the first element of the range (in this case v.begin()). This is no different to any other copying in C++. Would you consider the following to be a common mistake?

int x = some_other_int;

Or:

std::string str = some_other_string;

No, we write a declaration like this when we want a copy.

Here's an example use case:

void modify_argument(X&);
void use(X);

// ...

std::vector<X> v = /* ... */;
for (auto x : v) {
  // We want to modify the copy of x, but not the original:
  modify_argument(x);
  use(x);
}

相关问答

更多
  • template std::remove_reference_t const& as_const(T&&t){return t;} 可能有帮助。 返回右值的隐式共享对象可以隐式检测由于非常量迭代而导致的写入划分(和划分)。 这给你: for(auto&&item : as_const(foo())) { } 它可以让你以const方式迭代(并且很清楚)。 如果您需要参考寿命延长才能正常工作,请重复2次: template T const as_const(T&& ...
  • 实际上Boost确实有这样的适配器: boost::adaptors::reverse 。 #include #include #include int main() { std::list x { 2, 3, 5, 7, 11, 13, 17, 19 }; for (auto i : boost::adaptors::reverse(x)) std::cou ...
  • 可能有办法,但我非常怀疑它会更加优雅。 你在第一个循环中已经是正确的方法,限制了循环变量的范围/生命周期。 我会简单地忽略未使用的变量警告(毕竟,这只是编译器的一个迹象, 可能是错误的),或者使用编译器工具(如果有的话)可以在这一点上关闭警告。 There may be a way to do it but I very much doubt it would be more elegant. What you have in that first loop is already the correct w ...
  • 将枚举本身迭代为迭代器是一个糟糕的想法,我建议使用一个实际的迭代器,如在deft_code的答案中。 但如果这真的是你想要的: COLOR operator++(COLOR& x) { return x = (COLOR)(std::underlying_type::type(x) + 1); } COLOR operator*(COLOR c) {return c;} COLOR begin(COLOR r) {return COLOR::First;} COLOR end(COLOR r ...
  • 让我们开始区分观察连续性的元素,并修改它们。 观察元素 我们来看一个简单的例子: vector v = {1, 3, 5, 7, 9}; for (auto x : v) cout << x << ' '; 上面的代码打印vector的元素( int s): 1 3 5 7 9 现在考虑另一种情况,其中向量元素不仅仅是简单的整数,而是一个更复杂的类的实例,具有自定义复制构造函数等。 // A sample test class, with custom copy semantics. ...
  • 我们可以按值传递参数的原因完全相同 - 如果您需要副本,请按值获取。 我无法想象通过显式创建副本而不是为您执行循环而无法解决的任何问题。 我认为这是你遇到的主要问题。 循环不是“为你做”。 这是明确要求副本。 什么比初始化非参考变量更明确? 这实际上只是一个普通的声明。 为什么,当它在其他地方都有效时,我们会让auto本身无效吗? 实际上,该声明的初始化由标准定义为: auto x = *__begin; 其中__begin是赋予范围的第一个元素的迭代器的表达式(在本例中为v.begin() )。 这与C ...
  • cppreference.com页面指出,基于范围的for循环表达式生成类似于以下的代码( __range , __begin和__end仅用于说明): for( range_declaration : range_expression ) loop_statement { auto && __range = range_expression ; for (auto __begin = begin_expr, __end = end_expr; __begin != __end; ++__ ...
  • N4606(C ++ 17 draft)3.3.3 basic.scope.block,第4节说 在init-statement,for-range-declaration以及if,while,for和switch语句的条件中声明的名称是if,while,for或switch语句(包括受控语句)的本地名称,以及不得在该陈述的后续条件下,也不得在受控陈述的最外层(或对于if陈述,任何最外面的区块)重新宣布; 见6.4 缩短: 在... for-range-declaration ...中声明的名称是... f ...
  • 问题是QJsonArray的迭代器返回一个临时的QJsonValueRef对象,而左值引用无法绑定到临时对象。 我们可以按价值暂时保留: // QJsonArray oldArray contains an array of which one element is "bar" QJsonArray newArray; for (auto v : oldArray) { QJsonObject element = v.toObject(); element["foo"] = element[ ...
  • argv是指向原始字符串的指针数组,您无法直接从它获取范围。 你可以动态地将它转换为std::vector ,它与for-range循环一起使用: #include #include #include int main (int argc, char const * const argv[]) { for (auto && str : std::vector { argv, argv + ...

相关文章

更多

最新问答

更多
  • 获取MVC 4使用的DisplayMode后缀(Get the DisplayMode Suffix being used by MVC 4)
  • 如何通过引用返回对象?(How is returning an object by reference possible?)
  • 矩阵如何存储在内存中?(How are matrices stored in memory?)
  • 每个请求的Java新会话?(Java New Session For Each Request?)
  • css:浮动div中重叠的标题h1(css: overlapping headlines h1 in floated divs)
  • 无论图像如何,Caffe预测同一类(Caffe predicts same class regardless of image)
  • xcode语法颜色编码解释?(xcode syntax color coding explained?)
  • 在Access 2010 Runtime中使用Office 2000校对工具(Use Office 2000 proofing tools in Access 2010 Runtime)
  • 从单独的Web主机将图像传输到服务器上(Getting images onto server from separate web host)
  • 从旧版本复制文件并保留它们(旧/新版本)(Copy a file from old revision and keep both of them (old / new revision))
  • 西安哪有PLC可控制编程的培训
  • 在Entity Framework中选择基类(Select base class in Entity Framework)
  • 在Android中出现错误“数据集和渲染器应该不为null,并且应该具有相同数量的系列”(Error “Dataset and renderer should be not null and should have the same number of series” in Android)
  • 电脑二级VF有什么用
  • Datamapper Ruby如何添加Hook方法(Datamapper Ruby How to add Hook Method)
  • 金华英语角.
  • 手机软件如何制作
  • 用于Android webview中图像保存的上下文菜单(Context Menu for Image Saving in an Android webview)
  • 注意:未定义的偏移量:PHP(Notice: Undefined offset: PHP)
  • 如何读R中的大数据集[复制](How to read large dataset in R [duplicate])
  • Unity 5 Heighmap与地形宽度/地形长度的分辨率关系?(Unity 5 Heighmap Resolution relationship to terrain width / terrain length?)
  • 如何通知PipedOutputStream线程写入最后一个字节的PipedInputStream线程?(How to notify PipedInputStream thread that PipedOutputStream thread has written last byte?)
  • python的访问器方法有哪些
  • DeviceNetworkInformation:哪个是哪个?(DeviceNetworkInformation: Which is which?)
  • 在Ruby中对组合进行排序(Sorting a combination in Ruby)
  • 网站开发的流程?
  • 使用Zend Framework 2中的JOIN sql检索数据(Retrieve data using JOIN sql in Zend Framework 2)
  • 条带格式类型格式模式编号无法正常工作(Stripes format type format pattern number not working properly)
  • 透明度错误IE11(Transparency bug IE11)
  • linux的基本操作命令。。。