1. ホーム
  2. c

[解決済み] UNIXのノンブロッキングI/O。O_NONBLOCKとFIONBIOの比較

2023-02-18 13:56:21

質問

BSD ソケットプログラミングの文脈で遭遇するすべての例や議論において、 ファイルディスクリプタをノンブロッキング I/O モードに設定するための推奨方法は O_NONBLOCK フラグを fcntl() に変更する必要があります。

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

私は10年以上前からUNIXでネットワークプログラミングをしていますが、常に FIONBIO ioctl() の呼び出しで行ってきました。

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

なぜなのか、あまり考えたことはありません。 ただ、そのように学んだだけです。

どなたか、どちらか一方の利点についてコメントをお持ちではないでしょうか? ポータビリティの軌跡が多少異なることは想像できますが、その程度はわかりません。 ioctl_list(2) はそのような個々の ioctl メソッドを使用します。

どのように解決するのですか?

標準化以前は ioctl( ... FIONBIO ... ) そして fcntl( ... O_NDELAY ... ) といった具合に、システム間、あるいは同じシステム内でも挙動が不統一でした。 例えば、よくあるのは FIONBIO はソケットで動作し O_NDELAY で、パイプ、FIFO、デバイスのようなものには多くの不一致があります。 また、ファイル記述子の種類がわからない場合は、念のため両方を設定する必要がありました。 OS やファイル記述子の種類によっては、読み取りが 0 を返すこともあれば、 errno EAGAIN で -1、あるいは errno EWOULDBLOCK で -1 を返すこともあります。 今日でも FIONBIO または O_NDELAY は、データ無しの読み出しの場合、ttyやパイプでは0を、ソケットでは errno EAGAINで-1を返します。 ただし、0はEOFの場合にも返されるため、あいまいです。

POSIXでは、この問題を解決するために O_NONBLOCK を導入することで、異なるシステムやファイル記述子の種類にまたがって動作を標準化しました。 既存のシステムは通常、後方互換性を壊すかもしれない動作の変更を避けたいので、POSIX は他のどれかに対して特定の動作を強制するのではなく、新しいフラグを定義しました。 Linux のように 3 つすべてを同じように扱い、EAGAIN と EWOULDBLOCK を同じ値に定義するシステムもありますが、下位互換性のために他の古い動作を維持したいシステムは、古いメカニズムが使用されている場合にそうすることができます。

新しいプログラムでは fcntl( ... O_NONBLOCK ... ) というように、POSIXで標準化されています。