NetBSD Advent Calendar 2023 25日目の記事です。今日はカーネル内のプロセス情報からプロセスの親子関係のグラフを作成してみようと思います。
struct procに含まれているプロセス情報
NetBSDでのプロセスは/usr/src/sys/sys/proc.hで定義されている struct proc
構造体で管理されています。この構造体を見ると、struct proc->p_pptr
を辿ることで親プロセスの情報を参照できるようです。そして char p_comm
には実行しているコマンド名(=プロセス名)が入っているため、この2つの情報を参照することで、プロセスの親子関係をグラフ化できそうです。
230 struct proc {
...
265 struct proc *p_pptr; /* l: Pointer to parent process. */
...
335 char p_comm[MAXCOMLEN+1];
さっそくカーネルモジュールを作成してみる
動作としては、ユーザ空間からread(2)した際にGraphvizのグラフとしてプロセスの親子関係を返すという形にしてみます。
サンプルコードは以下になります。
ユーザ空間側のread(2)に対応するカーネルモジュール側の関数は以下になります。
93 int
94 process_graph_sample_read(dev_t self __unused, struct uio *uio, int flags __unused)
95 {
96 int e;
97 char result[4096] = { '\0' };
98 char tmp[128] = { '\0' };
99 char *r = result;
100
101 r = strcat(r, "digraph G {\n");
102
103 struct proc *p;
104 PROCLIST_FOREACH(p, &allproc) {
105 if (p->p_pid <= 1 || (p->p_flag & PK_SYSTEM) != 0) {
106 continue;
107 }
108 snprintf(tmp, 128, "\"%s\" -> \"%s\"", p->p_comm, p->p_pptr->p_comm);
109 r = strcat(r, tmp);
110 r = strcat(r, "\n");
111 }
112 r = strcat(r, "}\n");
113 r = strcat(r, "\0");
114
115 // 変数hostnameの値をユーザ空間にコピーする。
116 if ((e = uiomove(result, strlen(result), uio)))
117 return e;
118
119 return 0;
120 }
ポイントになる箇所…といっても特に何かあるワケではなく、以下の個所で親と子のプロセス名をdot形式のグラフ表記にしています。
108 snprintf(tmp, 128, "\"%s\" -> \"%s\"", p->p_comm, p->p_pptr->p_comm);
ユーザ空間側のコード
ユーザ空間からは単にread(2)するだけです。
実際に動かしてみる
# gcc -Wall -g -o process_graph_sample process_graph_sample.c
# ./process_graph_sample | tee output.dot
digraph G {
"sample" -> "bash"
"bash" -> "sshd"
"sshd" -> "sshd"
"getty" -> "init"
"getty" -> "init"
"getty" -> "init"
"getty" -> "init"
"cron" -> "init"
"inetd" -> "init"
"sshd" -> "init"
"powerd" -> "init"
"rpc.lockd" -> "init"
"rpc.statd" -> "init"
"rpcbind" -> "init"
"syslogd" -> "init"
"dhcpcd" -> "init"
}
PNG画像に変換します。
# dot -Tpng output.dot > output.png
プロセスの親子関係グラフが得られました。 inetd
や dhcpcd
、sshd
といったデーモンは init
プロセスから生まれており、ユーザが利用するシェルは sshd
から生まれていることが見て取れます。
まとめ
カーネル内のプロセス情報からプロセスの親子関係をグラフ化してみました。カーネルモジュール経由でカーネルの内部情報にアクセスする方法が把握できたので、今回のようなサンプル的なプログラムから一歩踏み込んだ実用的なツールやユーティリティ作成に応用できればと思います。
さいごに
今年も何とかNetBSD Advent Calendar 2023を完走することができました。これもひとえに皆様のおかげです。
@ryoon様、@oshimyja様、@ebijun様、@yamori813様(投稿日順です)ありがとうございました。
今年はAsiaBSDCon 2023も開催され、次期NetBSD-10に搭載される機能の紹介もあったことから、NetBSD-currentで来るべき新機能を調べたりしていました。wg(4)(WireGuard)を試してみたりしたかったのですが、ちょっと今回は調べる時間が足りませんでした。
とはいえ、今後もNetBSD-10のリリースまでは様々な機能の追加やバグ修正が行われるかと思いますので、来年はこまめにNetBSD情報を追いかけたり試したり、記事にしたりしてみようと思います。