使用带有O_DIRECT标志的libaio读取文件
Read file using libaio with O_DIRECT flag
我正在使用异步libio读取文件。下面的代码展示了我是如何做到的。代码运行良好,但现在我想切换到O_DIRECT模式,以避免文件缓存。当我将第25行更改为fd = open("。/测试文件",O_RDONLY | O_DIRECT);程序停止正常工作。(io_getevents不返回任何数据)。你能帮我调整一下程序,让它在O_DIRECT标志下也能正常工作吗?
Thanks in advance
操作系统:Ubuntu 12.10 3.5.0-26-generic
1 #include <unistd.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <sys/param.h>
7 #include <fcntl.h>
8 #include <errno.h>
9 #include <libaio.h>
10
11 int main(int argc, char* argv[]) {
12
13 struct iocb cb;
14 struct iocb* iocbs = &cb;
15 struct io_event events[1];
16 char data[4096];
17 io_context_t ctx;
18 int fd;
19 int res;
20
21 memset(&ctx, 0, sizeof(ctx));
22 memset(&cb, 0, sizeof(cb));
23 memset(&data, 0, sizeof(data));
24
25 fd = open("./testfile", O_RDONLY);
26
27 if(io_setup(1, &ctx) < 0) {
28 printf("io_setup errorn");
29 exit(-1);
30 }
31 printf("io_setup OKn");
32
33 // submit read request
34 io_prep_pread(&cb, fd, &data, 1024, 0);
35 res = io_submit(ctx, 1, &iocbs);
36 if(res < 0) {
37 printf("io_submit errorn");
38 exit(-2);
39 }
40 printf("io_submit OK: %dn", res);
41
42 // get events
43 res = io_getevents(ctx, 0, 1, events, NULL);
44 if(res < 0) {
44 if(res < 0) {
45 printf("io_getevents Error: %d", res);
46 exit(-3);
47 }
48 printf("io_getevents OK: %d %li %lin", res, events[0].res, events[0].res2);
49
50 // dump data received
51 char data_prefix[16];
52 strncpy(data_prefix, data, 15);
53 data_prefix[15] = 0;
54 printf("data: %sn", data_prefix);
55
56 res = io_destroy(ctx);
57 if(res < 0) {
58 printf("io_destroy Error: %dn", res);
59 exit(-4);
60 }
61 printf("io_destroy OK: %dn", res);
62
63 res = close(fd);
64 printf("close: %dn", res);
65 }
您需要将缓冲区对齐到512字节才能使O_DIRECT正常工作。前io_prep_pread
:
char *data;
posix_memalign(&data, 512, 1024);
请注意,读取的大小必须是512的倍数
Change char *data; to void* data;
那么做posix_memalign(及数据,512年,1024年),
您的示例有两个主要问题:
首先,调用io_getevents()
,将min_nr
设置为零:
43 res = io_getevents(ctx, 0, 1, events, NULL);
因此,如果在读取完成之前调用该函数,则该函数返回0个read事件是完全可以的。
参见io_getevents(2):
io_getevents()系统调用尝试读取至少min_nr个事件以及来自AIO上下文指定的完成队列的多达nr个事件
如果将min_nr
参数设置为1,即调用io_getevents(ctx, 1, 1, ...)
,则在读取尚未完成的情况下,函数会阻塞-直到它完成。
第二,你的读缓冲区不一定正确对齐O_DIRECT
,读大小可能不能被设备的扇区大小整除- cf. open(2)
:
O_DIRECT标志可能对长度和值施加对齐限制用户空间缓冲区的地址和I/o的文件偏移量。在Linux中对齐限制因文件系统和内核版本而异完全缺席。然而,目前还没有独立于文件系统的为应用程序发现控件的这些限制的凹接口给定的文件或文件系统。一些文件系统提供它们自己的接口中的XFS_IOC_DIOINFO操作xfsctl (3) .
在Linux 2.4下,传输大小和用户缓冲区的对齐并且文件偏移量必须都是逻辑块大小的倍数文件系统。从Linux 2.6.0开始,对齐到逻辑块大小的底层存储空间(通常为512字节)就足够了。逻辑块大小可以使用ioctl(2) BLKSSZGET操作来确定在shell中使用命令:
blockdev --getss
因此,这实际上取决于您的内核版本,文件系统和驱动器,您需要什么样的对齐方式,以及堆栈分配的data
数组是否碰巧按要求对齐。
类似这样的内容应该足以修复对齐和大小约束违规(除非您的设备有更大的物理扇区):
void *data = 0;
int r = posix_memalign(&data, 4096, 4096);
if (r) {
fprintf(stderr, "posix_memalign failed: %sn", strerror(r));
return 1;
}
请注意,如果您像这样更改分配,则必须调整相关的memset()
调用和其他用法。
您的示例中的其他问题:
您的错误处理不完整。这意味着你并不总是检查错误。在你做和报告他们的地方,你不把错误代码翻译成可读的东西(例如,通过适当的地方通过strerror()
传递代码)。
另外,有一行重复,导致了语法错误:
44 if(res < 0) {
44 if(res < 0) {
程序成功时退出状态未定义(即最后缺少return 0
),并且返回负退出状态是有问题的。
当您打印io_event::res
和res2
时,您使用了错误的格式说明符,正确的是%lu
;
- 即使使用调试编译标志,表达式也是"optimized out"
- 在 CMake 中为每个目标设置编译器/链接器标志
- File.cpp.o:OpenPose 标志 CMakeFiles/.. 的多重定义/main.cpp.o:首先在这里定
- 在轮班操作后使用携带标志
- 如何找出引入AVX标志的内容
- I2C 文件描述符上的 I2C 总线可写/可读标志
- C ++是否有C ++ 17 OSX 10.13.6的标志
- 每次使用带有LOCK_EX标志的LOCK_NB时,相同的程序/进程都会获取锁
- CMake 3.5 中的链接器标志位置
- 如何使用 gnu gcc 标志 -mpc32、-mpc64 和 -mpc80?
- 在 CMake 中添加全局编译标志
- -Wlifetime 标志的目的是什么?
- Visual Studio 19-17 库兼容性根据 GL 标志
- 标志 |= f == s[i],这个语句会做什么?
- Visual Studio 中是否有来自代码块的编译器标志的类似物?
- 如何在 clang 的自动会议中检查支持编译标志
- 使用 boost::p rogram_options 指定多个标志
- 如何告诉 gcc 显示您使用的优化标志列表
- 生成文件中隐式规则中的 -c 标志出错
- 使用带有O_DIRECT标志的libaio读取文件