2012-06-12 4 views
1

Я хочу сделать базовый read() с жесткого диска SATA /dev/sdd. A write() похоже работает. Также read() и write() работает без O_DIRECT Флаг. Я читал, что он должен быть привязан к блочному. Поэтому я использовал это, чтобы получить размер блока:чтение() с жесткого диска с ошибкой O_DIRECT с 22 (EINVAL, Invalid Argument)

root$ blockdev --getsize /dev/sdd 
488397168 

root$ blockdev --getsize64 /dev/sdd 
250059350016 

root$ python -c "print 250059350016.0/488397168" 
512.0 

Как вы можете видеть, у меня есть корень. Жесткий диск подключен через PCIe SATA Card, и lspci -vv показывает мне, что он использует базовый драйвер ahci (drivers/ata/ahci.c). Я работаю с ядром Linux 3.2.0 на 64-битной архитектуре питания.

Вот мой код:

#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 

редактировать: Я обновил свой источник, предложенный ответ Бретта Хейла. К сожалению, я все еще получаю ошибку. Могу ли я узнать, что в блоках нормально? Правильно ли я сделал выравнивание?

Большое спасибо за чтение,
Fabian

ответ

4

Прямая передача DMA обычно требуется буфер необходимо привести в соответствие. От man:

Флаг O_DIRECT может наложить ограничения выравнивания по длине и адрес в пользовательском пространстве буферов и смещения файла входов/выходов. ... В Linux 2.6 достаточно выравнивания до 512-байтных границ.

Возможно, что char buffer[512]; необходимо выровнять по 512-байтовому адресу.

Это не может быть возможным, чтобы достичь этого выравнивания в стеке, так что-то вроде:

static char buffer[512] __attribute__ ((__aligned__ (512)));

может работать. Или, возможно, это выравнивание будет работать в стеке. В качестве альтернативы, если вы используете x86, вы можете использовать встроенные функции поддержки <mm_malloc.h>: _mm_malloc и _mm_free.

+0

ahh Мне нужно выровнять мой буфер? Я думал, что позиция filepointer всегда должна быть выровнена (так что 0 в порядке), и получение 512 байтов для выравнивания по правильному пути - это правильный путь. Я обновил свой исходный код, но я все еще получаю сообщение об ошибке. Не могли бы вы быть добрыми и проверить, что я сделал? :) – samuirai

+0

@samuirai, есть интересная тема [здесь] (http://kerneltrap.org/node/7563) (2.6.x) с комментариями босса. –

+0

Да, я прочитал это. Для этого проекта очень важны ограничения скорости и мощности. Я должен рассказать о том, как быстро мы можем писать. Я слишком незнаю, чтобы задать вопрос: / – samuirai

0

BTW, выравнивание всегда не должно быть кратным 512 байт. это зависит от размера блока устройства. Вы должны найти его с помощью ioctl с BLKSSZGET. Если показания не совпадают с этим значением при использовании O_DIRECT, функция read() не будет работать с EINVAL.