systemptapでプロセスが所属する各pid名前空間におけるpid番号を取得する方法のめも。
プロセスのPIDは普通に見れますが、そこで見えているPIDというのは自身が所属しているPID名前空間においてのものです。なので、あるプロセスがunshareなりでpid名前空間を親プロセスと別の場合に、親プロセスの名前空間から見たPIDを知ることはできません。逆に親プロセスからは自身のPID名前空間での子プロセスのPIDを見ることはできますが、子プロセスのPID名前空間におけるPIDはわかりません。ってことで、あるプロセスが所属するPID名前空間でのPIDを全部見ることができるツールをsystemtapで書いてみました。
ソースはこちらです。stapのコマンドラインで
まず、dockerコンテナ内でnginxを動かしてみます。
[root@6d02a9c04d45 /]# ps -ef UID PID PPID C STIME TTY TIME CMD root 1 0 0 15:13 ? 00:00:00 /bin/bash root 51 1 0 15:15 ? 00:00:00 nginx: master process nginx http 52 51 0 15:15 ? 00:00:00 nginx: worker process root 59 1 0 15:19 ? 00:00:00 ps -ef
このときのホスト側ではプロセスはこう見えています。docker(2768)の下にnginxのプロセスがいますね。
$ pstree -p systemd(1)-+-agetty(259) |-agetty(260) |-dbus-daemon(235) |-dhcpcd(328) |-docker(2768)-+-bash(12494)---nginx(12572)---nginx(12573) | |-{docker}(2769) | |-{docker}(2770) | |-{docker}(2771) | |-{docker}(2772) | |-{docker}(2773) | |-{docker}(2774) | |-{docker}(2775) | |-{docker}(2776) | |-{docker}(2777) | |-{docker}(3019) | |-{docker}(3023) | |-{docker}(3024) | `-{docker}(3025) |-sshd(330)-+-sshd(1587)---sshd(1595)---bash(1596)---docker(12432)-+-{docker}(12433) | | |-{docker}(12434) | | |-{docker}(12435) | | |-{docker}(12436) | | |-{docker}(12437) | | |-{docker}(12438) | | |-{docker}(12439) | | `-{docker}(12532) | |-sshd(1674)---sshd(1676)---bash(1677)---pstree(12949) | `-sshd(12270)---sshd(12272)---bash(12273) |-systemd(1589)---(sd-pam)(1590) |-systemd-journal(169) |-systemd-logind(244) |-systemd-timesyn(192)---{sd-resolve}(195)
そして、ホスト側でスクリプトを動かすとこうなって、コンテナ内 -> ホストという感じでpidが表示できました。
masami@nlkb:~$ sudo stap -v -g ./pid.stp 12573 Pass 1: parsed user script and 113 library scripts using 69844virt/36104res/4924shr/31496data kb, in 90usr/10sys/109real ms. Pass 2: analyzed script: 2 probes, 2 functions, 1 embed, 0 globals using 70636virt/37136res/5180shr/32288data kb, in 10usr/0sys/3real ms. Pass 3: translated to C into "/tmp/stapfubRbf/stap_6dd962b4096d287515d5450e69c15b96_2149_src.c" using 70636virt/37136res/5180shr/32288data kb, in 0usr/0sys/0real ms. Pass 4: compiled C into "stap_6dd962b4096d287515d5450e69c15b96_2149.ko" in 1550usr/510sys/2322real ms. Pass 5: starting run. pid 52 pid 12573 Done Pass 5: run completed in 10usr/80sys/467real ms.
つぎはnsenterでdockerコンテナのpid名前空間に入り、そこでスクリプトを実行してみます。 まずはnsenter。
$ sudo nsenter -t 12494 -p
# ./tools/systemtap/bin/stap -v -g ./pid.stp 52 Pass 1: parsed user script and 113 library scripts using 69840virt/36220res/5048shr/31492data kb, in 100usr/10sys/105real ms. Pass 2: analyzed script: 2 probes, 2 functions, 1 embed, 0 globals using 70632virt/37252res/5304shr/32284data kb, in 0usr/0sys/3real ms. Pass 3: translated to C into "/tmp/stapRubZUb/stap_96165e2d709fb6669fa3a3854fe6f030_2146_src.c" using 70632virt/37252res/5304shr/32284data kb, in 0usr/0sys/0real ms. Pass 4: compiled C into "stap_96165e2d709fb6669fa3a3854fe6f030_2146.ko" in 1540usr/480sys/2312real ms. Pass 5: starting run. pid 52 pid 12573 Done Pass 5: run completed in 20usr/80sys/496real ms.
nsenterの場合、ファイルシステムは元の名前空間を使ってるので/procが完全に切り替わってません。なので、/procまで完全に切り離した状態でやってないのですが多分動くはず。。。
以下、systemtapのめもをば。 systemtapは実行時にコマンドライン引数を渡すことができます。今回は./pid.stp 52という感じでpidを渡しています。コマンドライン引数をprobeのところで使用するには$1や@1などで使用できます。$や@は型で、$は整数、@は文字列です。
このようにc言語で書いた関数にパラメータを渡しています。
probe begin { /* pass pid that is command line argument */ show_pidtree($1) exit() }
今回作ったスクリプトはc言語の関数を組み込んでいて、これも引数を取ることができます。使えるのはstringかlong型です。
function show_pidtree:long(pn:long) %{ %}
このスクリプトだとlong型のpnという引数を受け取ります。 ただ、引数はpnという名前ではアクセスできなくて(undefinedで怒られる)、STAP_ARGというprefixが必要です。 そのため、スクリプトでは下記のようにSTAP_ARG_pnとして引数のpnにアクセスしています。
int pid_nr = STAP_ARG_pn;
systemtapのスクリプトでは多少癖はありますが、c言語を使えるので便利ですね。
- 作者: Sam Newman,佐藤直生,木下哲也
- 出版社/メーカー: オライリージャパン
- 発売日: 2016/02/26
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (1件) を見る