0

私はプログラム、ある種のデータベースを書いています。のマニュアルを読んでいるときに、バッファをディスクにフラッシュfclose(3)するように呼び出していることがわかりました(実際には OS バッファですが、今は関係ありません。いつでも呼び出すことができます)。fflush(3)FILE*fsync(2)

私は DB を書いているので、データの損失を防ぎたいのは明らかです。ディスク容量がなく失敗fflush(3)したfclose(3)場合 — データが失われます。

FILE*エラーの後に使用fclose()すると、未定義の動作が発生します

fflush(3)そこで、 beforeを明示的に使用することを考えfclose(3)、ユーザーにディスク容量不足を警告fflush(3)し、しばらくしてから思い出すようにしました。

私はC標準を読んで、これは良い考えだと思いました。実際には、失敗した後fflush、2 番目の呼び出しは常に 0 (エラーなし) を返しますが、実際には何もしません。fsync役に立ちませんでした(データはRAMに保存されるのではないかと思いました)。

このような状況でデータの損失を防ぐにはどうすればよいですか? たぶん、いくつかの経験則があります。

ここに私のテストコードがあります:

#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;
}
4

4 に答える 4

3

後続の fflush() 操作が成功する理由は、ディスクに書き込む (新しい) データがないためです。最初の fflush() は失敗しました。それは悲劇ですが、歴史です。後続の fflush() は何もしないので、正常に実行されます。

データベースに書き込んでいる場合は、最後に問題を処理するだけでなく、書き込みごとに注意する必要があります。データの重要度に応じて、問題に対処するためにあらゆる種類の循環を経る必要がある場合があります。DBMS が複雑である理由はいくつかあり、書き込みの失敗もその 1 つです。

この問題に対処する 1 つの方法は、データ用のスペースを事前に割り当てることです。他の人が指摘しているように、従来の Unix ファイル システムではスパース ファイル (ディスク領域が割り当てられていない空のブロックがあるファイル) が許可されているため、割り当てが必要な各ページに実際にデータを書き込む必要があります。そうすれば、スペースを拡張するときに「ディスクがいっぱいになる」という問題だけを心配する必要があります。また、いつ拡張するかを知っているので、その失敗に慎重に対処できます。

Unix ベースのシステムでは、ディスク上のデータを同期するのに役立つさまざまなシステム コールや、「開く」オプションなどがあります。これらには、「O_DSYNC」および関連する値が含まれます。ただし、ファイルを拡張している場合は、派手な同期オプションを使用しても、「スペース不足」で失敗する可能性があります。そして、その失敗に遭遇した場合は、スペースが使用可能になるのを待って (おそらく、使用可能になったときにユーザーに知らせるように依頼したため)、書き込みを再試行する必要があります。

于 2010-02-07T01:05:03.227 に答える
1

fflush は C ライブラリの内部バッファを OS にフラッシュするだけなので、fflush はデータ損失がないことを保証しません。

fflush を繰り返し (中間の fwrite なしで) 呼び出しても、データは OS に一度フラッシュされているため、役に立ちません。2 番目の fflush 呼び出しは、OS にフラッシュするものが何もないため、SUCCESS を返します。ハードディスクがいっぱいで fflush が失敗した場合、すでに一部のデータが失われています。

データをディスクにフラッシュするには、fsync を使用する必要があります。

ハードディスクがいっぱいの場合、運が悪いです。データの損失を防ぐ唯一の方法は、ディスク上に fsync するスペースが見つかるまで、プロセス (およびメモリ内のデータ: ユーザー空間/カーネル ファイル バッファー) を維持することです。停電するとデータが消えてしまいます。

要するに、ハードディスクがいっぱいになった場合にデータが失われないことを保証する方法はありません。

于 2010-02-07T00:56:30.017 に答える
1

妥当な量のディスク容量を事前に割り当てることができます。いくつかのバイナリ ゼロ (またはその他のもの) を書き込み、フラッシュし、fsync してから、元の場所にシーク​​します。必要に応じてすすぎ、繰り返します。また、必要に応じて切り捨てることを忘れないでください。

少し痛いですが、うまくいくはずです。

于 2010-02-07T01:02:55.050 に答える