acceptエラーの原因がpipe

正月浮かれのなか、自宅サーバで動かしているサーバプロセスにエラーが大量発生して驚きました。
自宅サーバの環境はMacOSⅩでソースはCで組んだものでした。

エラーログ

[ERROR]Bad file descriptor
[ERROR]***************************************************
[ERROR]7時44分55秒
[ERROR]accept() にてエラー [pID:10126]
[ERROR]Bad file descriptor
[ERROR]***************************************************
[ERROR]7時44分55秒
[ERROR]accept() にてエラー [pID:10126]
[ERROR]Bad file descriptor
[ERROR]***************************************************
[ERROR]7時44分55秒
[ERROR]accept() にてエラー [pID:10126]
[ERROR]Bad file descriptor
[ERROR]***************************************************

acceptは、ソケット通信において、クライアントからアクセスがあったら、そのクライアントと1対1の通信ができるfile descriptorを生成してくれる関数です。[詳細説明]


ディスクリプタの上限数に達した?


エラーに出てきたようにaccpt関数ででも、ファイル・ディスクリプタを取得できます。クライアントマシンとの入出力に使うファイル・ディスクリプタです。

初めは1プロセスで使用可能なファイル・ディスクリプタの上限に引っかかったのかな?と思いました。

$ ulimit -n
256

これで1つのプロセスで利用可能なディスクリプタ数を確認できます。MacOSⅩでは256個がデフォルトで、ソケット通信時にはこれが支障になります。

accept関数により1クライアント1ファイル・ディスクリプタに関連付けされますが、上限があるのでクライアントが256人以上アクセスしてきた場合、acceptはエラーを返してしまうのです。

しかしログを見る限り、256ものクライアントは接続してきていません。
(そして上限も拡張済みだった)


原因はpipe


プログラムでは共有パイプを利用していました。
これがバグをはらんでいました。

http://linuxjm.sourceforge.jp/html/LDP_man-pages/man2/pipe.2.html
によると、

プロセス間通信に使用できる単方向のデータチャネルである。

「単方向」であることが義務づけられているということを知りませんでした。

入力側でデータを取り出した後、取り出したことをpipeに書き込んでしまっていました。入力側で書きこみしてしまうと単方向になりません。(入力側は入力しかできない、当然)

考えれば当然、UNIXコマンドでもデータが逆戻りすることなんてありません。

そして、おそらくは逆戻りしてしまったデータがファイル・ディスクリプタの管理領域を破壊していたのだと思います。

その状態で、パイプをcloseしたら、直後からacceptにエラーが発生しています。

パイプを単方向にちゃんとしたら、このエラーは発生しなくなりました。