18

知られているように、いくつかのブロッキング呼び出しは と のようreadwrite-1 を返し、 に設定さerrnoEINTR、これを処理する必要があります。

私の質問は次のとおりです。これは非ブロッキング呼び出しに適用されますか。たとえば、socket を に設定しますO_NONBLOCKか?

私が読んだいくつかの記事やソースでは、非ブロッキング呼び出しはこれを気にする必要はないと述べていますが、それについての信頼できる参照は見つかりませんでした。もしそうなら、それは異なる実装間で適用されますか?

4

3 に答える 3

30

この質問に対する決定的な答えを提供することはできません。答えはシステムによってさらに異なる可能性がありますが、ノンブロッキング ソケットがEINTR. bind()次のソケット関数、connect()send()、およびについてさまざまなシステムのマニュアル ページを調べたり、POSIX 標準でそれらを調べたりするとreceive()、興味深いことがわかり-1ます。失敗することが文書化されていない関数の 1 つが. また、デフォルトでは決してブロックされない、そのリストの唯一の機能です。したがって、 、 、を含むが原因で、ブロッキング関数のみが失敗する可能性があるようですerrnoEINTREINTRbind()bind()EINTRread()write()EINTR、ただし、これらの関数がブロックされない場合、それらが失敗することもありませんO_NONBLOCK

また、論理的な観点からも意味がありません。たとえば、ブロッキング I/O を使用していて、呼び出しread()を行い、この呼び出しをブロックする必要があるとします。ただし、ブロック中にシグナルがプロセスに送信され、読み取り要求がブロック解除されます。システムはこの状況をどのように処理する必要がありますか? read()それが成功したと主張しますか?それはうそでしょう、データが読み取られなかったので成功しませんでした。成功したと主張していますが、ゼロバイトのデータが読み取られましたか? 「ゼロ読み取り結果」はストリームの終わり (またはファイルの終わり) を示すために使用されるため、これも正しくありません。そのため、プロセスはデータが読み取られなかったと想定します。ファイルに到達した(またはソケット/パイプが反対側で閉じられた)が、これは単にそうではありません。呼び出した場合、ファイルの終わり (またはストリームの終わり) に達していません。read()繰り返しますが、より多くのデータを返すことができます。だからそれも嘘でしょう。この読み取り呼び出しが成功してデータを読み取るか、エラーで失敗することを期待しています。したがって、その場合、読み取り呼び出しは失敗して返さなければなりません-1が、システムはどのようなerrno値を設定するのでしょうか? 他のすべてのエラー値は、ファイル記述子の重大なエラーを示していますが、重大なエラーはなく、そのようなエラーを示すのも嘘です。errnoが に設定されている理由は次のとおりです。つまりEINTR「ストリームに問題はありませんでした。シグナルによって中断されたため、読み取り呼び出しが失敗しました。中断されていない場合は、まだ成功している可能性があります。それでも気にする場合は、データについては、もう一度やり直してください。」

ノンブロッキング I/O に切り替えると、上記の状況は発生しません。読み取り呼び出しは決してブロックされず、すぐにデータを読み取ることができない場合、エラーEAGAIN(POSIX) またはEWOULDBLOCK(非公式、Linux ではどちらも同じエラーであり、別の名前にすぎません) で失敗します。つまり、 「データがありません」という意味です。現在利用可能であるため、読み取り呼び出しはブロックしてデータが到着するのを待つ必要がありますが、ブロックは許可されていないため、代わりに失敗しました。」したがって、発生する可能性のあるすべての状況にエラーがあります。

もちろん、ノンブロッキング I/O であっても、読み取り呼び出しがシグナルによって一時的に中断された可能性がありますが、なぜシステムがそれを示さなければならないのでしょうか? これがシステム関数であるか、ユーザーによって作成された関数であるかに関係なく、すべての関数呼び出しは、シグナルによって一時的に中断される可能性があります。実際にはすべての関数が例外ではありません。それが発生するたびにシステムがユーザーに通知する必要がある場合、すべてのシステム機能が失敗する可能性がありEINTRます。ただし、信号の中断があったとしても、関数は通常、タスクを最後まで実行するため、この中断は無関係です。エラーEINTR信号の割り込みが原因で要求したアクションが実行されなかったことを呼び出し元に伝えるために使用されますが、非ブロック I/O の場合、関数が読み取りまたは書き込み要求を実行しない理由はありません。現時点では実行できませんが、適切なエラーによって示される可能性があります。

私の理論を確認するために、私は MacOS (10.8) のカーネルを調べました。これはまだ大部分が FreeBSD カーネルに基づいており、疑いを裏付けているようです。利用可能なデータがないために読み取り呼び出しが現在不可能な場合、カーネルO_NONBLOCKはファイル記述子フラグのフラグをチェックします。このフラグが設定されている場合、 ですぐに失敗しEAGAINます。設定されていない場合は、 という名前の関数を呼び出して、現在のスレッドをスリープ状態にしますmsleep()。関数はここに文書化されています(私が言ったように、OS X はそのカーネルで多くの FreeBSD コードを使用します)。この関数は、現在のスレッドが明示的に起動される (データが読み取り可能になる場合) か、タイムアウトになるまで (たとえば、ソケットに受信タイムアウトを設定できます)、現在のスレッドをスリープさせます。それでも、シグナルが配信された場合、スレッドも起動されます。その場合、msleep()それ自体が戻りEINTR、次の上位レイヤーがこのエラーを通過するだけです。そのためmsleep()EINTRエラーが発生しますが、O_NONBLOCKフラグが設定されている場合、msleep()そもそも呼び出されないため、このエラーを返すことはできません。

もちろんそれは MacOS/FreeBSD であり、他のシステムは異なる可能性がありますが、ほとんどのシステムはこれらの API 間で少なくとも一定レベルの一貫性を維持しようとしているため、システムが仮定を破った場合、ノンブロッキング I/O 呼び出しは決して失敗することはありません。ためEINTR、これはおそらく意図したものではなく、報告すれば修正される可能性さえあります。

于 2013-01-23T17:06:04.430 に答える