select システムコールには fd の待ち方として3種類のあって、どの fd でどの待ち方にするか指定するのに引数が3つ (readfds, writefds, errorfds) ある。
errorfds は exceptfds と呼ばれることもある。
readfds は読み込み可能になるまで待つ、writefds は書き込み可能になるまで待つ、 ということで、まぁわかる。(まぁ、read/write が可能な fd については)
しかし、errorfds はよくわからない。
TCP の out-of-band data とか、 pty で使われるという話は聞いたことがあるが、 実際に使ったことはない。
Linux man-pages の select(2) の exceptfds に関する記述には、 poll(2) の POLLPRI のところを読むようにかかれていて、 そこには TCP の out-of-band data, pty の packet mode, cgroup.events があげられている。 お、cgroup のは知らなかった。 しかし、使う機会はなさそう。
先日、さらに加えて Windows の non-blocking connect でも使う、という話を知った。 上にあげた機能と違って、non-blocking connect はかなり普通の機能なので、使うこともあるかもしれない。
SUSv4 では、non-blocking connect の結果は、writefds で待てる。 connect の結果が成功と失敗のどちらにせよ、writefds に指定しておけば、結果が出るのを select で待つことができる。
しかし、Windows では違うようで、成功を writefds で待ち、失敗を errorfds で待つことになっているようだ。
SUSv4 を読み直すと、成功と失敗のどちらでも writefds で検知できるとあるので、この Windows の挙動は SUSv4 には合致していないと思う。
ただそれはおいておいて、なぜこうなっているのか考えると、そのほうがシステムコールを減らせるからだろう。 select で connect の終了を検知した後、 成功か失敗か、失敗の場合はどんな理由 (errno) で失敗したのかを調べることになるのだが、 select の段階で成功か失敗かがわかれば、とくに成功の場合は、そのまま成功した場合の処理を始められる。
そういえば、select で connect の終了を検知した後、もう一回同じ引数で connect を呼び出すことで成功と失敗を区別できるのだが、 Unix では一回は成功する (connect の返値が 0 になる) のに、Windows ではすぐに EISCONN になるという話があった気がする
今でもそうなのかは知らないが、これも select で成功と失敗を区別できるからもう一回 connect を呼ぶ必要はないという考え方から来ているのかもしれない。
(なお、最近教えてもらったのだが、connect を再度呼ぶ以外に getsockopt でも成功か失敗かは調べられる)
[latest]