2

我想read()从 SATA HDD做一个基本的/dev/sdd。Awrite()似乎工作。也可以在没有标志的情况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 的回答更新了我的来源。不幸的是,我仍然得到错误。我找出块大小的方法可以吗?我做对了吗?

非常感谢您阅读,
费边

4

2 回答 2

5

直接 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.

于 2012-06-12T13:16:25.153 回答
0

顺便说一句,对齐总是不需要是 512 字节的倍数。这取决于设备块大小。您应该使用带有 BLKSSZGET 的 ioctl 找到它。如果在使用 O_DIRECT 时读取未与该值对齐,则 read() 将失败并显示 EINVAL。

于 2014-11-13T18:11:22.583 回答