7

だから私はかなり長い間fcloseマンページを勉強しています.私の結論は、マンページによると、fcloseが何らかの信号によって中断された場合、回復する方法はないということです...? 私はいくつかのポイントを逃していますか?

通常、バッファリングされていない POSIX 関数 (open、close、write など) では、呼び出しを再開することでシグナル割り込み (EINTR) から回復する方法が常にあります。対照的に、バッファリングされた呼び出しのドキュメントには、 fclose の試行が失敗した後、別の試行で未定義の動作が発生することが記載されています...代わりに回復する方法についてのヒントはありません。シグナルが fclose を中断した場合、私は「運が悪い」だけですか? データが失われる可能性があり、ファイル記述子が実際に閉じられているかどうかはわかりません。バッファが割り当て解除されていることは知っていますが、ファイル記述子はどうですか? 多数の fd を同時に使用し、fd が適切に解放されていない場合に問題が発生する大規模なアプリケーションについて考えてみてください -> この問題に対するクリーンな解決策が必要であると思います。

ライブラリを作成していて、sigaction と SA_RESTART の使用が許可されておらず、多くのシグナルが送信されていると仮定しましょう。fclose が中断された場合、どうすれば回復できますか? fclose が EINTR で失敗した後、(fclose の代わりに) ループで close を呼び出すのは良い考えでしょうか? fclose のドキュメントでは、ファイル記述子の状態について言及していません。ただし、 UNDEFINEDはあまり役に立ちません... fd が閉じられているときにもう一度 close を呼び出すと、デバッグが困難な奇妙な副作用が発生する可能性があるため、当然、このケースは間違ったことをしているので無視したいと思います...利用可能なファイル記述子の数に制限はなく、リソースのリークはある種のバグです (少なくとも私にとっては)。

もちろん、fclose の特定の実装を確認することはできますが、誰かが stdio を設計し、この問題について考えなかったとは信じられませんか? 悪いのはドキュメントだけですか、それともこの関数の設計ですか?

このコーナーケースは本当に私を悩ませます:(

4

2 に答える 2

5

EINTRclose()

close()実はだけでなく にも問題がありますfclose()

POSIX は をclose()返すEINTRと述べています。これは通常、アプリケーションが呼び出しを再試行できることを意味します。ただし、Linux では状況がより複雑になります。LWN に関するこの記事と、この投稿も参照してください。

[...]POSIXEINTRセマンティクスは、Linux では実際には不可能です。に渡されたファイル記述子close()は、システム コールの処理の早い段階で割り当てが解除され、同じ記述子が戻るまでに別のスレッドに渡されている可能性がありclose()ます。

このブログ投稿この回答close()では、なぜ失敗したかを再試行するのが得策ではない理由を説明していEINTRます。したがって、Linux では(または)close()で失敗した場合、意味のあることは何もできません。EINTREINPROGRESS

close()また、Linux では非同期であることに注意してください。たとえば、カーネルでまだ解放されていないため、ファイルシステムで最後に開いた記述子を閉じた直後にumount戻る場合があります。EBUSYここで興味深い議論を参照してください:ページ 1ページ 2


EINTRfclose()

の POSIX 状態fclose():

の呼び出し後、fclose()ストリームを使用すると、未定義の動作が発生します。

呼び出しが成功するかどうかに関係なく、ストリームはファイルから関連付けが解除され、setbuf()またはsetvbuf()関数によって設定されたバッファはストリームから関連付けが解除されます。関連付けられたバッファが自動的に割り当てられた場合は、割り当てが解除されます。

たとえclose()失敗したとしても、すべてのリソースfclose() 解放し、リークを発生させないことを意味すると思います。少なくともglibcuclibc の実装には当てはまります。


信頼できるエラー処理

  • fflush()の前に呼び出しfclose()ます。

    またはを呼び出しfclose()たときに失敗したかどうかを判断できないため、前に明示的に呼び出して、ユーザー空間バッファーがカーネルに正常に送信されたことを確認する必要があります。fflush()close()fflush()fclose()

  • の後に再試行しないでくださいEINTR

    fclose()で失敗するとリトライEINTRできず、リトライclose()もできませんfclose()

  • fsync()必要な場合はお電話ください。

    • データの整合性を気にする場合は、1を呼び出す前にfsync()orを呼び出す必要があります。fdatasync()fclose()
    • そうでない場合は、EINTRfrom を無視してfclose()ください。

ノート

  • fflush()fsync()成功し、 でfclose()失敗した場合EINTRデータは失わず、リークも発生しません。

  • また、オブジェクトが と別のスレッドからの呼び出しの間でFILE使用されないようにする必要があります2fflush()fclose()


[1]記事「Fsync() について常に知りたいことすべて」fsync()を参照してください。この記事では、非同期操作である可能性がある理由を説明しています。

[2] と を呼び出す前に呼び出すことができflockfile()ます。正しく動作するはずです。fflush()fclose()fclose()

于 2015-07-28T08:44:23.487 に答える
2

多数の fd を同時に使用し、fd が適切に解放されていない場合に問題が発生する大規模なアプリケーションについて考えてみてください。

再試行してから基になるファイル記述子を使用する可能性fflush()についてclose()は、コメントで既に言及されています。大規模なアプリケーションの場合、スレッドを使用し、専用のシグナル処理スレッドを 1 つ持つパターンを好みますが、他のすべてのスレッドは を使用してシグナルをブロックしpthread_sigmask()ます。次に、fclose()失敗すると、実際の問題が発生します。

于 2015-07-21T06:18:41.210 に答える