我正在使用异步libaio读取文件。 下面的代码显示了我是如何做到的。代码工作正常, 但现在我想切换到O_DIRECT模式以避免文件缓存。 当我将第25行更改为fd = open(“./ testfile”,O_RDONLY | O_DIRECT)时; 程序停止正常工作。 (io_getevents不返回任何数据)。 你能帮我调整一下程序,以便它也能正常使用O_DIRECT标志吗?
提前致谢
OS: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 error\n");
29 exit(-1);
30 }
31 printf("io_setup OK\n");
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 error\n");
38 exit(-2);
39 }
40 printf("io_submit OK: %d\n", 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 %li\n", 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: %s\n", data_prefix);
55
56 res = io_destroy(ctx);
57 if(res < 0) {
58 printf("io_destroy Error: %d\n", res);
59 exit(-4);
60 }
61 printf("io_destroy OK: %d\n", res);
62
63 res = close(fd);
64 printf("close: %d\n", res);
65 }
答案 0 :(得分:2)
您需要将缓冲区与512字节对齐,以使O_DIRECT正常工作。在io_prep_pread
之前:
char *data;
posix_memalign(&data, 512, 1024);
请注意,读取的大小必须是512的倍数。
答案 1 :(得分:0)
更改char *data; to void* data;
答案 2 :(得分:0)
您的示例有两个主要问题:
首先,您调用 io_getevents()
并将 min_nr
设置为零:
43 res = io_getevents(ctx, 0, 1, events, NULL);
因此,如果您在读取完成之前调用该函数,则返回 0 个读取事件完全没问题。
<块引用>io_getevents() 系统调用尝试读取至少 min_nr 个事件 以及来自 AIO 上下文指定的完成队列的最多 nr 个事件 由 ctx_id 提供。
如果您将 min_nr
参数设置为 1,即调用 io_getevents(ctx, 1, 1, ...)
,则函数会阻塞以防读取尚未完成 - 直到完成。
其次,对于 O_DIRECT
,您的读取缓冲区不一定正确对齐 - 参见。 open(2)
:
O_DIRECT 标志可能会对长度和 用户空间缓冲区的地址和 I/O 的文件偏移量。在 Linux 中 对齐限制因文件系统和内核版本而异,并且可能 完全不在。但是目前没有文件系统独立的 dent 接口,用于应用程序发现这些限制的应用程序 给定的文件或文件系统。一些文件系统提供了它们自己的互 这样做的面孔,例如 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: %s\n", strerror(r));
return 1;
}
请注意,如果您像这样更改分配,您还必须调整相关的 memset()
调用和其他用法。
您的示例的其他问题:
您的错误处理不完整。这意味着您并不总是检查错误。在您执行和报告的地方,您不会将错误代码转换为可读的内容(例如,在适当的情况下通过 strerror()
传递代码)。
另外,有一行重复出现了语法错误:
44 if(res < 0) {
44 if(res < 0) {
程序成功时的退出状态是未定义的(即最后缺少 return 0
)并且返回一个否定的退出状态是有问题的。
当您打印 io_event::res
和 res2
时,您使用了错误的格式说明符,正确的是 %lu
;