UNIX domain socket での net.core.somaxconn や sk->sk_max_ack_backlog の実装を調べていた際に、 ss
コマンドでソケットのバックログのサイズと accept(2) 待ちのソケット数を取れることを知った
以下、検証と実装を追いかけた記録
検証環境
検証用コード
簡易の UNIX Domain Socket サーバーをたてる。バックログのサイズをシェルから指定できる
require 'socket' s = UNIXServer.new("/tmp/server.sock") s.listen(ARGV[0].to_i) sleep 3600
backlog のサイズを 10 にする
$ ruby server.rb 10
$ ss -ax | grep -e Netid -e tmp Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port u_str LISTEN 0 10 🌟 /tmp/server.sock 90740 * 0
Send-Q が 10 になっている
backlog のサイズを 128 にする
$ ruby server.rb 128
$ ss -ax | grep -e Netid -e tmp Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port u_str LISTEN 0 128 🌟 /tmp/server.sock 90753 * 0
Send-Q が 128に増えた。 Send-Q とバックログのサイズ ( listen(2) で指定する ) が対応していることが分かる
Recv-Q は何を示す数値か?
Recv-Q は accpet(2) 待ちのソケットの数である ( カーネルのソースを読んで そのように解釈した )
検証
nc でソケットに繋いで見ると Recv-Q の数値が増えるのが確認できる
$ nc -U /tmp/server.sock & $ nc -U /tmp/server.sock & $ nc -U /tmp/server.sock &
一切 accept(2) しないサーバに nc で3つほど繋ぐ
$ ss -ax | grep -e Netid -e tmp Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port u_str LISTEN 3 🌟 128 /tmp/server.sock 91350 * 0
Recv-Q が 3 になっている
Recv-Q > Send-Q の時に ノンブロッキングモードで connect(2) すると EAGAIN - Resource temporarily unavailable を返すので、エラーの際にはこの数値を見て監視なり確認ができるだろう
Recv-Q と Send-Q の数値はどうやって取るのか?
strace で ss -ax
をトレースすると netlink を使っている
) = 211socket(PF_NETLINK, SOCK_RAW, 4) = 3 sendto(3, "(\0\0\0\24\0\1\3@\342\1\0\0\0\0\0\1\0\0\0\377\17\0\0\0\0\0\0\25\0\0\0"..., 40, 0, NULL, 0) = 40 recvfrom(3, "H\0\0\0\24\0\2\0@\342\1\0\220_\0\0\1\1\n\0XL\0\0\0^\33:\0\210\377\377"..., 8192, 0, {sa_family=AF_NETLINK, pid=0, groups=00000000}, [12]) = 3768write(1, "u_str LISTEN 0 100 "..., 211u_str LISTEN 0 100 private/smtp 19544
これを手がかりにあれこれ調べたら、netlink についてはカーネルの net/unix/diag.c
で実装されていた。
Linux/net/unix/diag.c - Linux Cross Reference - Free Electrons
Recv-Q と Send-Q の数値を取る部分を見てみると、下記のようなコードになっている
static int sk_diag_show_rqlen(struct sock *sk, struct sk_buff *nlskb) { struct unix_diag_rqlen rql; if (sk->sk_state == TCP_LISTEN) { rql.udiag_rqueue = sk->sk_receive_queue.qlen; 🌟 Recv-Q rql.udiag_wqueue = sk->sk_max_ack_backlog; 🌟Send-Q } else { rql.udiag_rqueue = (u32) unix_inq_len(sk); rql.udiag_wqueue = (u32) unix_outq_len(sk); } return nla_put(nlskb, UNIX_DIAG_RQLEN, sizeof(rql), &rql); }
2011年に 以下のパッチで追加された機能のようだ
sk->sk_receive_queue と sk->sk_max_ack_backlog がどのような変数なのかは また別の機会に