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