Pergunta

Eu estou escrevendo um programa, algum tipo de banco de dados. Enquanto eu estava lendo o manual de fclose(3) eu achei que ele chama fflush(3) para buffers FILE* liberar para o disco (na verdade, a tampão OS, mas isso não importa agora, podemos sempre chamar fsync(2)).

Porque eu estou escrevendo um DB é óbvio que eu quero evitar a perda de dados. Se não houver espaço em disco e fflush(3) em fclose(3) falhar - vamos perder nossos dados, porque

usando FILE* após um erro na fclose() irá causar um comportamento indefinido

Então eu pensei sobre o uso explícito de fflush(3) antes fclose(3), avisar o usuário sobre espaço em disco baixa e fflush(3) recordação depois de um tempo.

Eu li o C padrão e pensei que esta era uma boa idéia. Na prática, após fflush falha a segunda chamada seria sempre retornar 0 (nenhum erro), mas que, na verdade, não fazer nada. fsync não me ajudar (eu pensei que os dados podem ser salvos na memória RAM).

Como posso evitar a perda de dados em tal situação uma? Talvez existam algumas regras de ouro.

Aqui está o meu código de teste:

#include <stdio.h>
int main()
{
    FILE *a = fopen("/tmp/1", "wb")
    if ( !a )
        perror("fopen");

    if ( fwrite("test", 1, 4, a) != 4 )
        perror("fwrite");  // always OK, cause data is buffered


    while( fflush(a) )  // ...second call will always return 0!
    {
        perror("fflush");  // if there is no disk space, I will get this perror, but ...
    }


    if ( fclose(a) )  // always ok, because calls only close(2)
        perror("fclose"); 

    return 0;
}
Foi útil?

Solução

A razão da fflush subseqüente () operações de sucesso é que não há dados (novo) para gravar em disco. O primeiro fflush () falhou; que é trágico, mas a história. O fflush subseqüente () não tem nada para fazer, então fá-lo com sucesso.

Se você está escrevendo para um banco de dados, você tem que ter cuidado com cada gravação - não apenas lidar com problemas no final. Dependendo do grau de importância seus dados, você pode precisar de passar por todos os tipos de oscilações para lidar com problemas - há razões para que DBMS são complexos, e escreve que falharam são um deles

.

Uma forma de lidar com o problema é a pré-alocar o espaço para os dados. Como outros já mencionado, os sistemas de arquivos clássicos Unix permitem arquivos esparsos (arquivos onde há blocos vazios sem espaço em disco alocado para eles), então você realmente tem que gravar alguns dados em cada página que você precisa alocado. Então você só tem que se preocupar com os problemas dos discos completos "quando você estender o espaço -. E você sabe quando você faz isso e você pode lidar com essa falha com cuidado

sistemas baseados em Unix On, há uma variedade de chamadas do sistema que podem ajudar a sincronizar os dados no disco, e opções para 'abrir' etc. Estes incluem o 'O_DSYNC' e valores relacionados. No entanto, se você está estendendo um arquivo, eles ainda podem causar falhas para 'fora do espaço', mesmo com as opções de sincronização de fantasia. E quando você se deparar com que a falha, você tem que esperar para o espaço se torne disponível (porque você pediu o usuário para dizer-lhe quando ele estiver disponível, talvez), e tente a gravação novamente.

Outras dicas

Você pode pré-alocar uma certa quantidade razoável de espaço em disco. Escrever, flush, e fsync alguns zeros binários (ou qualquer outro) e, em seguida, buscar de volta para onde você estava. Enxágüe e repita quando necessário. E lembre-se para truncar se necessário.

Um pouco de dor, mas ele deve funcionar.

fflush só vai liberar os buffers internos C biblioteca para o sistema operacional, assim que um fflush não vai garantir que não haverá perda de dados.

Chamando fflush repetidamente (sem fwrites intermediários) não ajuda, como você já liberados os dados para o sistema operacional uma vez. A segunda chamada fflush retornará sucesso como existe nada para liberar para o sistema operacional. Se fflush falha porque disco rígido está cheio, você já perdeu alguns dados.

Para liberar os dados para o disco, você necessidade usar fsync.

Se o disco rígido está cheio, você está fora de sorte. A única maneira de evitar a perda de dados é manter agora o seu processo vivo (e dados na memória: tanto no espaço do usuário / buffers de arquivo do kernel) até encontrar algum espaço no disco para fsync para. Agora, se o poder sai, você irá perder os dados.

Em suma, não há nenhuma maneira você pode garantir nenhuma perda de dados se o disco rígido está cheio.

Você poderia fseek (3) até o fim do arquivo (supondo que você saberia o comprimento), antes de fazer qualquer coisa. Dessa forma, você iria eliminar a possibilidade de falha devido a espaço insuficiente no disco.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top