从具有 O_DIRECT 的 HDD 读取()失败并显示 22(EINVAL,无效参数)
Posted
技术标签:
【中文标题】从具有 O_DIRECT 的 HDD 读取()失败并显示 22(EINVAL,无效参数)【英文标题】:read() from a HDD with O_DIRECT fails with 22 (EINVAL, Invalid Argument) 【发布时间】:2012-06-15 07:52:37 【问题描述】:我想从 SATA HDD /dev/sdd
制作一个基本的 read()
。 write()
似乎有效。 read()
和 write()
也可以在没有 O_DIRECT
标志的情况下工作。我读过,它必须与块大小对齐。所以我用它来获取块大小:
root$ blockdev --getsize /dev/sdd
488397168
root$ blockdev --getsize64 /dev/sdd
250059350016
root$ python -c "print 250059350016.0/488397168"
512.0
如您所见,我有根。硬盘通过 PCIe SATA 卡连接,lspci -vv
向我展示了它使用基本的 ahci (drivers/ata/ahci.c
) 驱动程序。
我在 64 位 Power Architecture 上使用 3.2.0 Linux 内核。
这是我的代码:
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
int main()
int r;
char *buffer, *tmp_buffer;
// alloc more than necesarry to align the real buffer
tmp_buffer = malloc(2*512*sizeof(char));
long align = (unsigned long)tmp_buffer%512;
printf("tmp_buffer is at: %x \% 512 = %d\n",tmp_buffer,align);
buffer = tmp_buffer+(512-align);
printf("buffer is at: %x \% 512 = %d\n",buffer,(unsigned long)buffer%512);
memset(buffer,0,sizeof(512));
// OPEN
int fd = open("/dev/sdd",O_DIRECT | O_RDWR | O_SYNC);
if(fd!=3) printf("fd = %d\n",fd);
// READ
printf("try to read and then dump buffer:\n");
r = read(fd,buffer,sizeof(512));
if(r == -1) printf("Error: %s\n",strerror(errno));
else
// DUMP BUFFER
int i;
for(i=0; i<sizeof(512); i++)
printf("%c",buffer[i]);
printf("\n");
return 0;
输出是:
tmp_buffer is at: 1cc80010 % 512 = 16
buffer is at: 1cc80200 % 512 = 0
try to read and then dump buffer:
Error: Invalid argument
编辑: 我已经按照 Brett Hale 的回答更新了我的来源。不幸的是,我仍然得到错误。我找出块大小的方法可以吗?我做对了吗?
非常感谢您的阅读, 费边
【问题讨论】:
【参考方案1】:直接 DMA 传输通常需要对齐缓冲区。来自man
:
O_DIRECT 标志可能对长度和地址施加对齐限制 用户空间缓冲区的数量和 I/O 的文件偏移量。 ... 在 Linux 2.6 下,对齐到 512 字节边界就足够了。
所以char buffer[512];
可能需要与一个 512 字节的地址对齐。
可能无法在堆栈上实现这种对齐,因此类似于:
static char buffer[512] __attribute__ ((__aligned__ (512)));
可能会起作用。或者这种对齐方式可能会在堆栈上起作用。或者,如果您使用的是 x86,则可以使用 <mm_malloc.h>
内在支持函数:_mm_malloc
和 _mm_free
。
【讨论】:
啊我必须对齐我的缓冲区?我认为文件指针的位置必须始终对齐(所以 0 可以),并且在后面对齐 512 字节是正确的方法。我已经更新了我的源代码,但我仍然得到错误。你会这么好心并检查我做了什么吗? :) @samuirai,有一个有趣的线程 here (2.6.x) 来自老板的 cmets。 是的,我读过这个。对于这个项目,速度和电源故障证明非常重要。我必须测量我们能写多快的时间。我太不知道了,无法质疑这个:/【参考方案2】:顺便说一句,对齐总是不需要是 512 字节的倍数。这取决于设备块大小。您应该使用带有 BLKSSZGET 的 ioctl 找到它。如果在使用 O_DIRECT 时读取未与此值对齐,则 read() 将失败并返回 EINVAL。
【讨论】:
以上是关于从具有 O_DIRECT 的 HDD 读取()失败并显示 22(EINVAL,无效参数)的主要内容,如果未能解决你的问题,请参考以下文章
writev(或pwritev)会与c中的O_DIRECT冲突吗?
`O_DIRECT | 有啥区别? O_SYNC` + write() 和 `O_DIRECT` + write() + fsync()