web-dev-qa-db-ja.com

UNIXノンブロッキングI / O:O_NONBLOCK対FIONBIO

BSDソケットプログラミングのコンテキストで遭遇するすべての例と議論では、ファイル記述子を非ブロッキングI/Oモードに設定する推奨方法は、O_NONBLOCKフラグをfcntl()に使用することです。

int flags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);

私はUNIXで10年以上ネットワークプログラミングを行ってきましたが、これを行うには常にFIONBIO ioctl()呼び出しを使用しました。

int opt = 1;
ioctl(fd, FIONBIO, &opt);

なぜ本当に多くのことを考えたことはありません。その方法で学んだだけです。

誰かがそれぞれの可能性のあるメリットについてのコメントを持っていますか?移植性の軌跡は多少異なると思いますが、ioctl_list(2)が個々のioctlメソッドのその側面に対応していないため、どの程度までわからないのでしょうか。

88
Alex Balashov

標準化の前には、ioctl(...FIONBIO...)およびfcntl(...O_NDELAY...)、しかし、これらはシステム間で一貫性がなく、同じシステム内でも動作していました。たとえば、FIONBIOがソケットで動作し、O_NDELAYがttyで動作するのが一般的でしたが、パイプ、fifo、およびデバイスのようなものには多くの矛盾がありました。そして、あなたがどんな種類のファイル記述子を持っていたか知らなかったなら、あなたは確実に両方を設定しなければなりません。しかし、さらに、利用可能なデータがない非ブロッキング読み取りも一貫性のないものとして示されました。 OSとファイル記述子のタイプに応じて、読み取りは0、errno EAGAINの場合は-1、errno EWOULDBLOCKの場合は-1を返します。今日でも、SolarisでFIONBIOまたはO_NDELAYを設定すると、データなしで読み取りが行われ、ttyまたはパイプでは0が返され、ソケットではerrno EAGAINで-1が返されます。ただし、0はEOFに対しても返されるため、あいまいです。

POSIXはO_NONBLOCKの導入によりこれに対処しました。これは、さまざまなシステムおよびファイル記述子タイプにわたって動作を標準化しています。既存のシステムは通常、後方互換性を損なう可能性のある動作の変更を避けたいため、POSIXは特定の動作を他のいずれかに強制するのではなく、新しいフラグを定義しました。 Linuxのような一部のシステムは、3つすべてを同じように扱い、EAGAINとEWOULDBLOCKを同じ値に定義しますが、下位互換性のために他のレガシー動作を維持したいシステムは、古いメカニズムを使用するときにそうすることができます。

新しいプログラムでは、POSIXで標準化されているfcntl(...O_NONBLOCK...)を使用する必要があります。

127
mark4o

fcntl()はPOSIX関数だと思います。 ioctl()は標準のUNIXのものです。以下に POSIX io のリストを示します。 ioctl()は非常にカーネル/ドライバー/ OS固有のものですが、使用するものはほとんどの種類のUnixで動作するはずです。いくつかの他のioctl()ものは、特定のOSまたはそのカーネルの特定のリビジョンでのみ動作する可能性があります。

6
EdH

@Seanが言ったように、fcntl()は大部分が標準化されているため、プラットフォーム間で利用可能です。 ioctl()関数は、Unixではfcntl()よりも前ですが、標準化されていません。 ioctl()があなたに関連するすべてのプラットフォームで機能したことは幸運ですが、保証されていません。特に、2番目の引数に使用される名前は不可解であり、プラットフォーム間で信頼性がありません。実際、それらは多くの場合、ファイル記述子が参照する特定のデバイスドライバーに固有のものです。 (たとえば、20年前にPNX(Perq Unix)を実行しているICL Perqで実行されているビットマップグラフィックデバイスに使用されるioctl()呼び出しは、他のどこにも変換されません。

5