BSDの top(1) でブロックしたsyscall名が出る理由

追記: BSD系の top(1) は,ブロックするsyscallでスリープ状態の時は,"STATE" 欄が "S" じゃなくて具体的なsyscallの名前が出るようになってる? どうなってんだこれは.Linuxでも可能なのかな? それともtask structが違うんだろうか? 新鮮な驚き.暇があったら調べてみよう.

http://upload.wikimedia.org/wikipedia/commons/2/20/BSD-unix-top-plain.png

昨日「Debian Squeezeの最小インストール後Wheezyにバージョンアップしてからデスクトップ環境入れようとしたら tasksel(8) がなぜか終わらないー」って困ってた友人がいて,まぁそれは結局 keyring 入れてなかったせいでプロンプトが出てブロックしてたらしいんだが,それに気付くまでに彼が悩んでた中で「スリープ状態のプロセスが具体的にどのsyscallでブロックされたんだか分かったらいいのに!」というのがあった.(もちろん最初からやり直せるなら strace(1) を使えばよいが.)ということでこの話を思い出した.
Wikipedia経由の知識だがBSDtop(1) は以下から落とせる(まぁ pkgsrc 手元にあったらソースもURLもあるから問題なかろうが):

top.c で参照されてる内部インタフェイスのうち,ここでは format_next_process() が重要(その前段の get_process_info() も見るべき).具体的な実装はプラットフォームごと分かれてて,NetBSDだと machine/m_netbsd.c にある.

        struct kinfo_proc2 *pp;
(snip)
        if (pp->p_stat == SSLEEP) {
                strlcpy(wmesg, pp->p_wmesg, sizeof(wmesg));
                statep = wmesg;
        } else
                statep = state_abbrev[(unsigned)pp->p_stat];

つまりNetBSDだと kinfo_proc2 なる構造体の p_wmesg なるメンバでブロックしたsyscallの名前が分かるらしい.これは kvm_getproc2(3) で取得できる.

見ると分かるが(呼び出し方が複数あり得るようでややこしいけど)この中はと言えば kvm_getprocs(3) 経由で結局 sysctl(2) を呼んでる:

                size = 0;
                mib[0] = CTL_KERN;
                mib[1] = KERN_PROC;
                mib[2] = op;
                mib[3] = arg;
                st = sysctl(mib, 4, NULL, &size, NULL, (size_t)0);
                if (st == -1) {
                        _kvm_syserr(kd, kd->program, "kvm_getprocs");
                        return (NULL);
                }
                KVM_ALLOC(kd, procbase, size);
                st = sysctl(mib, 4, kd->procbase, &size, NULL, (size_t)0);

引数の mib とやらは結局 { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 } となるようだ.MIB (Management Information Base) ってSNMPの専門用語かと思ってたらそうじゃないのね.

この中のディスパッチを追うのはまた今度だなー.本当はそこが知りたい中核なんだけど.
なおNetBSDソースのGit版はこちら: