LibAio技术疑问:未调用io_getevents为何仍能读取到数据?
LibAio 使用疑问解答:未调用
io_getevents 却读取到数据的原因及规范用法 1. 调用 io_submit 方法后,是否必须调用 io_getevents 方法?
是,必须调用。io_submit 仅负责将IO请求提交到内核队列,不会等待IO操作完成,也不会同步返回操作结果。如果不调用 io_getevents,内核中对应的IO上下文资源无法被正确回收,会造成资源泄漏;同时你无法确认IO请求是否真的完成、是否成功,程序行为会处于未定义状态。
2. 若必须调用,为何会出现未调用却成功获取数据的情况?
这是内核同步完成优化导致的巧合:
- 当读取的文件很小,或者文件内容已被操作系统缓存到内存中时,内核会直接在
io_submit调用过程中完成数据拷贝,不需要异步等待磁盘IO。 - 这种行为完全依赖系统状态(缓存情况、文件大小、内核调度策略),属于不可预期的“副作用”,绝对不能作为正常逻辑依赖。如果读取大文件或缓存未命中的文件,
double_check_m_buf中不会有有效数据,甚至程序会崩溃或出现乱码。
3. 何时调用 io_getevents 获取读取结果?能否多次调用该方法?
- 调用时机:提交IO请求后,当你需要获取IO操作的结果(成功/失败状态、实际读取字节数),或者需要等待IO完成再继续后续逻辑时,调用
io_getevents。通常会在io_submit后,通过循环调用io_getevents等待所有提交的请求完成。 - 是否可多次调用:完全可以。
io_getevents每次调用可以获取一批已完成的IO事件,你可以根据返回的完成事件数,循环调用直到所有提交的请求都被处理完毕。
修正后的演示代码(添加 io_getevents 调用)
#include <stdio.h> #include <libaio.h> #include <sys/stat.h> #include <fcntl.h> #include <string.h> #include <unistd.h> #define error() printf("error [%s : %d]\n", __FILE__, __LINE__) #define BUFF_SIZE 51 #define BUFF_CNT 50 #define READLEN 4194304 int main(int argc, char *argv[]) { int fd = open(__FILE__, O_RDONLY); if (fd == -1) { error(); return -1; } io_context_t ctx = 0; int err = io_setup(BUFF_CNT, &ctx); if (err != 0) { error(); return -1; } struct iocb *io = (struct iocb *)malloc(sizeof(struct iocb)); if (NULL == io) { printf("alloc struct iocb failed"); return -1; } memset(io, 0x00, sizeof(struct iocb)); // 修正原代码错误:sizeof(io)是指针大小,改为结构体实际大小 char double_check_m_buf[READLEN]; memset(double_check_m_buf, 0, READLEN); // 初始化缓冲区,避免脏数据干扰 io_prep_pread(io, fd, double_check_m_buf, READLEN, 0); int rc = io_submit(ctx, 1, &io); if (rc < 0) { printf("aio send read one block failed"); return -1; } // 调用io_getevents等待IO完成 struct io_event events[1]; int ret = io_getevents(ctx, 1, 1, events, NULL); if (ret > 0) { // 检查IO是否成功 if (events[0].res > 0) { printf("aio read completed, actual read len: %ld \n content: %.*s", events[0].res, (int)events[0].res, double_check_m_buf); } else { printf("aio read failed, error code: %ld\n", events[0].res); } } else { printf("io_getevents failed\n"); } // 清理资源 io_destroy(ctx); free(io); close(fd); return 0; }
注意:原演示代码存在一处错误:
memset(io, 0x00, sizeof(io))中sizeof(io)是指针的大小(通常8字节),而非struct iocb的实际大小,会导致结构体未完全初始化,修正后改为sizeof(struct iocb)。
内容的提问来源于stack exchange,提问作者hui li
相关产品推荐
相关产品推荐

