今いるシェルなり何なりで、親プロセスを手繰って行きたい時がたまにある。
“ps —forest” や “pstree -hp” でも良いんだけど、もう少しシンプルで軽いのが欲しかったので、親プロセスをパンくずリスト風に表示するプログラムをawkで書いてみた。
使い方
スクリプトを実行すると、その実行環境のプロセスから親プロセスを手繰ってパンくずリスト風に表示する。
[root@localhost ~]# ./pbreadcrumbs
init(1) > sshd(1488) > sshd(32639) > bash(32642)
以下に示すようにpid 32642は現在のシェル環境。
[root@localhost ~]# echo $$
32642
例えば、Ansibleとか、何かの延長で呼び出しても良い。
うまく使うと、プロセスコールのコールツリー/バックトレース的に使うこともできる。
うまく使うと、プロセスコールのコールツリー/バックトレース的に使うこともできる。
[ansibleman@controller ~]$ ansible -i inventory *.64 -m script -a ./pbreadcrumbs
192.168.170.64 | success >> {
"changed": true,
"rc": 0,
"stderr": "",
"stdout": "init(1) > sshd(1488) > sshd(33216)\n"
}
コード
pbreadcrumbsのコードを示す。とてもシンプル。
#!/bin/awk -f
BEGIN {
cur_pid = PROCINFO["ppid"] # Find the caller's process id.
n = 0
do {
statfile = sprintf("/proc/%d/stat",cur_pid)
getline < statfile # Now, $2 is name, and $4 is ppid.
err = close(statfile) # gawk extension
if (err)
break
name = gensub("[()]","","g",$2)
stack[n++] = sprintf("%s(%d)",name,cur_pid);
cur_pid = $4
} while(name!="init" && n<10)
for (i = n-1; i>0; i--)
printf "%s > ",stack[i]
printf "%s\n",stack[0]
}
getline で
/proc/<pid>/stat
を読み、プロセス名と親プロセスのpidを取得して、親へ遡るループを回る。途中で運悪く存在しないプロセスに行き当たってしまった場合、errが非0になるので、そこでループを終了している。最後のブロックは逆順でのパンくずリストの表示。性能
ps や pstree は、基本的に全プロセスの一覧を最初に取得し、その後にソートなりツリー構築するので、プロセスが多いと重たい処理になる。メモリフットプリントも大きい。
例えば、3000プロセスを生成してpstreeを実行してみる。
[root@localhost ~]# time pstree -h > /dev/null
real 0m0.255s
user 0m0.117s
sys 0m0.137s
同じ環境で pbreadcrumbs を実行してみる。
[root@localhost ~]# time pbreadcrumbs
init(1) > sshd(1488) > sshd(32639) > bash(32642)
real 0m0.003s
user 0m0.002s
sys 0m0.000s
pstreeはCで、pbreadcrumbsはスクリプト言語だけれど、後者は数回しかループを回らないから軽い。
プロセスを増やすテストプログラム(おまけ)
性能テストで使ったシェルスクリプト ./manyproc.sh を参考に示す。
モダンに? inotify-tools パッケージを使って書いてみた。
モダンに? inotify-tools パッケージを使って書いてみた。
#!/bin/bash
# You must set sysctl limit to allow $MAXFORK. For example,
# sysctl -w fs.inotify.max_user_instances=3500
MAXFORK=3000
STOPFILE=./DELETE_TO_STOP
touch $STOPFILE
for ((i=0;i<$MAXFORK;i++))
do
# inotify-tools required.
inotifywait -e delete_self $STOPFILE >/dev/null 2>&1 </dev/null &
done
echo "inotifywait forked $i times."
echo "delete $STOPFILE to quit the forked processes"
実行方法:
[root@localhost ~]# sysctl -w fs.inotify.max_user_instances=3500
fs.inotify.max_user_instances = 3500
[root@localhost ~]# ./manyproc.sh
inotifywait forked 3000 times.
delete ./DELETE_TO_STOP to quit the forked processes
[root@localhost ~]# pgrep inotify | wc -l
3000
止め方:
[root@localhost ~]# rm -f DELETE_TO_STOP
inotifyはとっても便利です。
コメントを投稿