select の semantics

なんか要は select で write 待ちしてる時に、帰ってきた file descriptor に何バイトまで書けるか、という話。

http://shinh.skr.jp/m/?date=20081008#p01

のへんに適当にはったりしたリンクによると、ポータビリティ考えるなら非同期使え、ってのが明らかに正しいぽい。 POSIX なんかを見てみると、

http://www.opengroup.org/onlinepubs/009695399/functions/select.html

If a descriptor refers to a socket, the implied output function is the sendmsg() function supplying an amount of normal data equal to the current value of the SO_SNDLOWAT option for the socket.

って書いてあって、えーとなんだこの難解な英語は! たぶん select から帰ってきた fd は SO_SNDLOWAT までは書けるって書いてあるんだと思う。となるとまぁ先は予想できると思うのですが、みんな大好き linux ではこの値が 1000 バイト程度はあるのだろう…と思って man 7 socket:

These two values are initialized to 1.

がーん

SO_SNDLOWAT is not changeable on Linux (setsockopt(2) fails with the error ENOPROTOOPT).

がーん。実際 Linux でチェックしてみたら 1 でした。

FreeBSD の man とか見ると、

The default value for SO_SNDLOWAT is set to a convenient size for network efficiency, often 1024.

らしく、なんか FreeBSD でチェックしてみたら 2048 とかかえってきました。

まぁこのへんまでが規格とかそいうレベルの話で、実際 linux でどうなってるのよ、ということだと、 kosaki さんが調べてくださった通り、 pipe に書く場合は 4096B は書けそう。

http://mkosaki.blog46.fc2.com/blog-entry-677.html

どっちかというと興味があったのは socket なので、ちょっと読んでみました… linux-2.6.25.4/net/ipv4/tcp.c

unsigned int tcp_poll(struct file *file, struct socket *sock, poll_table *wait)
{
    /* ... */
        if (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk)) {
            mask |= POLLOUT | POLLWRNORM;
        } else {  /* send SIGIO later */

この 2 つの関数は include/net/sock.h に定義されていて、

static inline int sk_stream_wspace(struct sock *sk)
{
    return sk->sk_sndbuf - sk->sk_wmem_queued;
}

static inline int sk_stream_min_wspace(struct sock *sk)
{
    return sk->sk_wmem_queued >> 1;
}

うーんこの sk_stream_min_wspace の方は本当にこの名前でいいんかいなー。でまぁ結論としてはキューが sk->sk_sndbuf の 2/3 以上埋まってる時は select から出てこない、ってことだと思う。てか snd とか send の略なんだろうけど 1byte だけ省略するとか理解に苦しむなー。どうせやるならゴ略

で、あとは sk_sndbuf のサイズがわかればいいんだと思う。ちょっと grep すると sysctl な値から取ってきてるぽい(めどくなってきてソースコードの引用とか略)

> cat /proc/sys/net/ipv4/tcp_wmem
4096    16384   4194304

で、左が min で真ん中が default で右が max 。まぁ 16k / 3 で 5k 程度は書けるってことかなー。

これ Ruby で軽く実験してみた結果と全然あわない。てかコレ Ruby もたぶん、さっきの akr さんがほげほげとかで、バッファ作ったりとかしてそうだったので、まぁなんかそいうので変わるんじゃないかな。 1.8 と 1.9 でも違うし。というわけで誰か C で実験してください。てか TODO

あと Unix ドメインソケットとかはどうなんですかね。

static inline int unix_writable(struct sock *sk)
{
        return (atomic_read(&sk->sk_wmem_alloc) << 2) <= sk->sk_sndbuf;
}

知らんけど sk_wmem_alloc はたぶんバッファの量だとすると、 sk_sndbuf はデフォルトぽい

 #define SOCK_MIN_SNDBUF 2048

から変えてる気配がないので、たぶん 512B かな。

正直カンで読んだので自信がない。ほとんど実験するコードとか書いてないので書くべきだと思う。

追記:

なんか Ruby でやってみてわけわからん挙動してたと思ったのは Ruby が悪いんじゃなくて普通に linux がそういうふうになってる、ってことですかね…

http://cvs.m17n.org/~akr/diary/2008-10.html#a2008_10_10_1

まぁやはし何も仮定すんなってことですね…

なにかあれば下記メールアドレスへ。
shinichiro.hamaji _at_ gmail.com
shinichiro.h