0

我正在使用异步 lib​​aio 读取文件。下面的代码显示了我是如何做到这一点的。代码工作正常,但现在我想切换到 O_DIRECT 模式以避免文件缓存。当我将第 25 行更改为 fd = open("./testfile", O_RDONLY|O_DIRECT); 程序停止正常工作。(io_getevents 不返回任何数据)。您能帮我调整程序,使其在 O_DIRECT 标志下也能正常工作吗?

提前致谢

操作系统: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 }
4

3 回答 3

2

您需要将缓冲区对齐到 512 字节才能使 O_DIRECT 正常工作。之前 io_prep_pread

char *data;
posix_memalign(&data, 512, 1024);

请注意,读取的大小必须是 512 的倍数。

于 2013-09-12T09:44:47.797 回答
0

您的示例有两个主要问题:


首先,您调用io_getevents()设置min_nr为零:

43   res = io_getevents(ctx, 0, 1, events, NULL);

因此,如果您在读取完成之前调用它,该函数返回 0 个读取事件是完全可以的。

另见io_getevents(2)

io_getevents() 系统调用尝试从 ctx_id 指定的 AIO 上下文的完成队列中读取至少 min_nr 个事件和最多 nr 个事件。

如果您将min_nr参数设置为 1,即io_getevents(ctx, 1, 1, ...)在读取尚未完成的情况下调用然后函数阻塞 - 直到它完成。


其次,您的读取缓冲区不一定正确对齐,O_DIRECT并且读取大小可能无法被设备的扇区大小整除 - cf。open(2)

O_DIRECT 标志可能会对用户空间缓冲区的长度和地址以及 I/O 的文件偏移施加对齐限制。在 Linux 中,对齐限制因文件系统和内核版本而异,并且可能完全不存在。然而,目前还没有独立于文件系统的接口供应用程序发现给定文件或文件系统的这些限制。一些文件系统为此提供了自己的接口,例如 xfsctl(3) 中的 XFS_IOC_DIOINFO 操作。

在 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;

于 2021-05-15T11:42:30.670 回答
0

改变char *data; to void* data;

然后做 posix_memalign(&data,512,1024);

于 2017-10-31T15:16:02.293 回答