天泣記

2024-07-23 (Tue)

#1 select の errorfds (exceptfds) の利用例

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]


田中哲