我想从SATA HDD read()
制作一个基本的/dev/sdd
。 write()
似乎有效。 read()
和write()
也可以在没有O_DIRECT
标志的情况下工作。我已经读过,它必须与blocksize对齐。所以我用它来获取块大小:
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的回答更新了我的来源。不幸的是我仍然得到错误。我的方法是找出块大小吗?我做对齐了吗?
非常感谢你阅读,
费边
答案 0 :(得分:4)
直接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
。
答案 1 :(得分:0)
顺便说一句,对齐始终不需要是512字节的倍数。这取决于设备块大小。您应该使用带有BLKSSZGET的ioctl找到它。如果在使用O_DIRECT时读取未与此值对齐,则read()将因EINVAL而失败。