ss で UNIX domain socket のバックログのサイズと accept(2) 待ちのソケット数を取る

UNIX domain socket での net.core.somaxconn や sk->sk_max_ack_backlog の実装を調べていた際に、 ss コマンドでソケットのバックログのサイズと accept(2) 待ちのソケット数を取れることを知った

以下、検証と実装を追いかけた記録

検証環境

  • CentOS 7.1611 (7.3) CR
  • 3.10.0-514.el7.x86_64
検証用コード

簡易の 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年に 以下のパッチで追加された機能のようだ

www.spinics.net

sk->sk_receive_queue と sk->sk_max_ack_backlog がどのような変数なのかは また別の機会に