Desativar o cache / buffer de leitura para o dispositivo de armazenamento em massa USB no Linux

0

Eu tenho um dispositivo USB que se apresenta como um dispositivo de armazenamento em massa, o dmesg se parece com isso:

[    4.416584] scsi 0:0:0:0: Direct-Access     Adap ECU Modular ECU      1.0  PQ: 0 ANSI: 2
[    4.420186] sd 0:0:0:0: [sda] 131072 512-byte logical blocks: (67.1 MB/64.0 MiB)
[    4.421063] sd 0:0:0:0: [sda] Write Protect is off
[    4.421084] sd 0:0:0:0: [sda] Mode Sense: 03 00 00 00
[    4.422053] sd 0:0:0:0: [sda] No Caching mode page found
[    4.422067] sd 0:0:0:0: [sda] Assuming drive cache: write through
[    7.446823] sd 0:0:0:0: Attached scsi generic sg0 type 0

Ele não contém um sistema de arquivos, nem é suposto. Você não monta, você simplesmente abre e lê (neste caso) / dev / sda

O problema é quando abro e leio o dispositivo, recebo o que quero da primeira vez, e depois cada leitura subseqüente recebo (o que eu suponho) dados armazenados em cache. Eu faço essa suposição porque a luz IO no dispositivo não pisca após as leituras subseqüentes, somente na primeira leitura e, claro, porque meu fread() contém exatamente os mesmos dados que o primeiro fread() .

Meu código é bom porque eu o compilei no Windows e especifiquei \. \ X: para que o arquivo fosse aberto e ele pudesse pesquisar os dados muito bem com novos dados em cada chamada.

Então, vou assumir que o Linux está armazenando em cache / armazenando em buffer a leitura.

Meu código é assim:

#define STARTBYTE 272384
#define ENDBYTE 274432
#define NUM_VARS 1024

int main() {
    FILE *input = fopen("/dev/sda", "r+");
    setbuf(input, NULL);
    int exit = 0;
    while (exit < 1) {
        signed short liveBuffer[NUM_VARS];
        fseek(input, STARTBYTE, SEEK_SET);
        fread(liveBuffer, 2, NUM_VARS, input);
        fflush(input);
        // do stuff with the now filled liveBuffer data
    }
    fclose(input);
    return 0;
}

Eu também tentei com open() e especificando O_DIRECT , sem diferença.

O objetivo é que eu abra o dispositivo (/ dev / sda), procure 272384 bytes. Leia 2048 bytes. Procure novamente na mesma posição, leia 2048 bytes, etc etc.

Se eu fclose() do arquivo e re open() , recebo novos dados. Exceto que é lento. Aproximadamente 10 amostras / seg, quando no Windows (sem o abrir / fechar) eu fico em torno de 50 / seg.

Acho que notei que quando deixo meu código compilado rodar por alguns segundos e depois o mato (CTRL + C), vejo a luz de atividade no dispositivo piscar loucamente por quanto tempo deixo meu código correr para.

Alguém pode me apontar na direção certa?

Eu tenho trabalhado nisso há dias, acho que estou prestes a me enforcar.

    
por Josh Finlay 21.06.2017 / 12:44

1 resposta

1

Você complicou um pouco o assunto usando a camada intermediária stdio e ignorando os códigos de retorno das funções que está chamando. Você pode usar O_DIRECT para obter dados novos do dispositivo, mas deve respeitar as obrigações que isso impõe.

Em particular, o offset de busca, endereço de buffer e tamanho de i / o devem ser múltiplos de 4096 (ou alguma outra potência de 2 dependendo do dispositivo).

Se você modificar seu código para remover sua declaração atual de liveBuffer e incluir o código a seguir no início, você deve achar que funciona.

#define PAGE 4096
#define STARTBYTE (272384/PAGE*PAGE) // must align
#define OFFSET (272384-STARTBYTE)
#define ITEMSIZE (sizeof(*liveBuffer))
#define LIVEBUFSIZE ((OFFSET+NUM_VARS*ITEMSIZE+PAGE-1)/PAGE*PAGE)

signed short *liveBuffer;
if(posix_memalign((void**)&liveBuffer, PAGE, LIVEBUFSIZE)!=0)
   exit(5);
if (fcntl(fileno(input), F_SETFL, O_DIRECT) == -1)
   exit(6);

Você deve agora fread() usando LIVEBUFSIZE/ITEMSIZE em vez de NUM_VARS. Como o STARTBYTE precisa estar alinhado, agora você precisa começar a usar o liveBuffer array em OFFSET/ITEMSIZE para encontrar os dados desejados. Você também deve alterar todas as chamadas para verificar os códigos de retorno corretos.

    
por 22.06.2017 / 17:01

Tags