pstreeコマンド
pstreeはプロセスを親子関係のツリーとして表示するコマンド。psの一覧では分かりにくい「どのプロセスがどこから起動されたか」をひと目で把握できる。
$ pstree
systemd-+-bash---2*[sleep]
|-dbus-daemon
|-nginx---4*[nginx]
|-rsyslogd---3*[{rsyslogd}]
|-sh---top
|-sshd---sshd---sshd---sh---sleep
|-systemd---(sd-pam)
|-systemd-journal
|-systemd-logind
`-systemd-resolve
引数を省略するとPID 1のsystemdを起点に、システム全体のプロセスを表示する。読み方は次のとおり。
| 表記 | 意味 |
|---|---|
A---B | AがBの親プロセス |
A-+-B | Aに複数の子があり、続く行に残りの子が並ぶ |
2*[sleep] | 同じ形の枝が2つあるためまとめた |
{rsyslogd} | プロセスではなくスレッド |
上の例ではsshd---sshd---sshd---sh---sleepの並びから、SSHでログインしたセッションがsleepを起動したと読み取れる。
インストール
pstreeはpsmiscパッケージに含まれる。
$ sudo apt install psmisc
RHEL系のディストリビューションではdnfを使う。
$ sudo dnf install psmisc
PIDを表示する(-p)
-p(--show-pids)を付けると、プロセス名の後ろにPIDが表示される。killする対象を探す場合に使う。
$ pstree -p
systemd(1)-+-bash(142)-+-sleep(144)
| `-sleep(145)
|-dbus-daemon(82)
|-nginx(91)-+-nginx(93)
| |-nginx(94)
| |-nginx(95)
| `-nginx(96)
|-rsyslogd(98)-+-{rsyslogd}(105)
| |-{rsyslogd}(106)
| `-{rsyslogd}(108)
|-sh(255)---top(256)
|-sshd(223)---sshd(300)---sshd(311)---sh(312)---sleep(313)
|-systemd(229)---(sd-pam)(230)
|-systemd-journal(24)
|-systemd-logind(87)
`-systemd-resolve(73)
PIDはプロセスごとに異なるため、-pを付けると2*[sleep]のようなまとめ表示は無効になる。
コマンドライン引数を表示する(-a)
-a(--arguments)は起動時の引数まで表示する。同じ名前のプロセスを見分ける場合に使う。
$ pstree -a
systemd
|-bash -c sleep 3000 & sleep 3000 & wait
| |-sleep 3000
| `-sleep 3000
|-dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only
|-nginx
| |-nginx
| |-nginx
| |-nginx
| `-nginx
|-rsyslogd -n -iNONE
| `-3*[{rsyslogd}]
|-sh -c top -b -d 60 >/dev/null 2>&1
| `-top -b -d 60
|-sshd
| `-sshd
| `-sshd
| `-sh -c sleep 3000
| `-sleep 3000
|-systemd --user
| `-(sd-pam)
|-systemd-journal
|-systemd-logind
`-systemd-resolve
-pと組み合わせると、PIDはカンマ区切りでプロセス名の直後に付く。
$ pstree -pa 300
sshd,300
`-sshd,311
`-sh,312 -c sleep 3000
`-sleep,313 3000
まとめ表示をやめる(-c)
デフォルトでは同じ構造の枝を4*[nginx]のようにまとめる。-c(--compact-not)を付けると1つずつ展開する。
$ pstree -c
systemd-+-bash-+-sleep
| `-sleep
|-dbus-daemon
|-nginx-+-nginx
| |-nginx
| |-nginx
| `-nginx
|-rsyslogd-+-{rsyslogd}
| |-{rsyslogd}
| `-{rsyslogd}
|-sh---top
|-sshd---sshd---sshd---sh---sleep
|-systemd---(sd-pam)
|-systemd-journal
|-systemd-logind
`-systemd-resolve
ワーカープロセスの数を目視で数えたい場合に役立つ。
ユーザーの切り替わりを表示する(-u)
-u(--uid-changes)は、親と実行ユーザーが変わる箇所にユーザー名を表示する。
$ pstree -u
systemd-+-bash(alice)---2*[sleep]
|-dbus-daemon(messagebus)
|-nginx---4*[nginx(www-data)]
|-rsyslogd(syslog)---3*[{rsyslogd}]
|-sh(alice)---top
|-sshd---sshd---sshd(alice)---sh---sleep
|-systemd(alice)---(sd-pam)
|-systemd-journal
|-systemd-logind
`-systemd-resolve(systemd-resolve)
nginxはrootのマスタープロセスがwww-dataのワーカーを起動している。SSHの例では3段目のsshdでaliceに切り替わり、そこから先が利用者の権限で動いている。デーモンが意図した権限まで落ちているかの確認に使える。
PID順に並べる(-n)
デフォルトの並び順は名前順である。-n(--numeric-sort)を付けるとPID順、つまりおおよその起動順に並ぶ。
$ pstree -n
systemd-+-systemd-journal
|-systemd-resolve
|-dbus-daemon
|-systemd-logind
|-nginx---4*[nginx]
|-rsyslogd---3*[{rsyslogd}]
|-bash---2*[sleep]
|-sshd---sshd---sshd---sh---sleep
|-systemd---(sd-pam)
`-sh---top
最後に起動したプロセスを探す場合は末尾を見る。
表示範囲を絞る
引数にPIDを渡すと、指定したプロセスを根としたツリーだけを表示する。
$ pstree -p 91
nginx(91)-+-nginx(93)
|-nginx(94)
|-nginx(95)
`-nginx(96)
引数にユーザー名を渡すと、指定したユーザーが実行するプロセスを根としたツリーだけが並ぶ。
$ pstree -p alice
bash(142)-+-sleep(144)
`-sleep(145)
sh(255)---top(256)
sshd(311)---sh(312)---sleep(313)
systemd(229)---(sd-pam)(230)
誰がどんなプロセスを残しているかを調べる場合に便利である。
デフォルトの起点はPID 1のため、カーネルスレッドはツリーに現れない。カーネルスレッドはPID 2のkthreadd配下にあり、pstree 2で確認できる。
$ pstree 2
kthreadd-+-cpuhp/0
|-cpuhp/1
|-cpuhp/2
|-cpuhp/3
|-ecryptfs-kthread
親をたどる(-s)
-s(--show-parents)は指定したプロセスの祖先をたどって表示する。暴走しているプロセスの出所を調べる場合に使う。
$ pstree -ps 313
systemd(1)---sshd(223)---sshd(300)---sshd(311)---sh(312)---sleep(313)
PID 313はsshd経由のログインセッションから起動されたと分かる。cronやsystemdのサービスから起動されたプロセスであれば、祖先にcronやsystemdが現れる。
指定したプロセスに子がある場合、祖先に加えて配下のツリーもあわせて表示される。
$ pstree -ps 91
systemd(1)---nginx(91)-+-nginx(93)
|-nginx(94)
|-nginx(95)
`-nginx(96)
スレッドを表示する(-t/-T)
デフォルトではスレッドを{プロセス名}の形で表示する。
$ pstree -p 98
rsyslogd(98)-+-{rsyslogd}(105)
|-{rsyslogd}(106)
`-{rsyslogd}(108)
-t(--thread-names)を付けると、プロセス名の代わりにスレッド名を表示する。どのスレッドがCPUを使っているかを調べる場合の手がかりになる。
$ pstree -tp 98
rsyslogd(98)-+-{in:imklog}(106)
|-{in:imuxsock}(105)
`-{rs:main Q:Reg}(108)
逆にスレッドが邪魔な場合は-T(--hide-threads)で隠す。
$ pstree -T
systemd-+-bash---2*[sleep]
|-dbus-daemon
|-nginx---4*[nginx]
|-rsyslogd
|-sh---top
|-sshd---sshd---sshd---sh---sleep
|-systemd---(sd-pam)
|-systemd-journal
|-systemd-logind
`-systemd-resolve
プロセスグループIDを表示する(-g)
-g(--show-pgids)はPIDの代わりにプロセスグループIDを表示する。
$ pstree -g
systemd(1)-+-bash(142)---2*[sleep(142)]
|-dbus-daemon(82)
|-nginx(91)---4*[nginx(91)]
|-rsyslogd(98)---3*[{rsyslogd}(98)]
|-sh(255)---top(255)
|-sshd(223)---sshd(300)---sshd(300)---sh(312)---sleep(312)
|-systemd(229)---(sd-pam)(229)
|-systemd-journal(24)
|-systemd-logind(87)
`-systemd-resolve(73)
sleepの値が親のbashと同じ142になっている。kill -- -142のようにプロセスグループ単位でシグナルを送る場合、どこまで巻き込むかを事前に確認できる。
pstree --helpには-gが-cを含むと書かれているが、PGIDが同じ枝は2*[sleep(142)]のようにまとめて表示される。
現在のプロセスを強調する(-h/-H)
-h(--highlight-all)はpstree自身とその祖先を太字で強調する。今いるシェルがツリーのどこにあるかを確認する場合に使う。
$ pstree -h
-H PID(--highlight-pid)は指定したプロセスとその祖先を強調する。目的のプロセスを大きなツリーの中から見つけやすくなる。
$ pstree -H 91
強調には端末のエスケープシーケンスを使う。-hは環境変数TERMが未設定でもエラーにならず、強調のない出力になる。-HはTERMが未設定ならTERM is not setのエラーで終了する。TERM=dumbのように強調表示を持たない端末では、どちらもエラーにならず強調のない出力になる。
強調が使えない環境では、現在のシェルの位置をpstree -ps $$で確認する方法もある。
罫線の文字を変える(-A/-G/-U)
デフォルトの描画文字は出力先とロケールで決まる。標準出力が端末で、かつロケールの文字コードがUTF-8であればUTF-8の罫線素片を使う。パイプやファイルへの出力やCロケールではASCII文字を使う。-A(--ascii)を付けると、環境によらずASCII文字で固定できる。
$ pstree -A
systemd-+-bash---2*[sleep]
|-dbus-daemon
|-nginx---4*[nginx]
-U(--unicode)はUTF-8の罫線素片を使う。
$ pstree -U
systemd─┬─bash───2*[sleep]
├─dbus-daemon
├─nginx───4*[nginx]
VT100の罫線を使う-G(--vt100)もある。端末やフォントによって罫線素片が崩れる場合は-Aで固定する。本記事の出力例はパイプ経由で採取したため、すべてASCII文字で表示されている。
長い行を切り詰めない(-l)
デフォルトでは1行が端末の幅に合わせて切り詰められる。-aと組み合わせると引数の途中で切れてしまう。
$ pstree -pa 425
python3,425 -c import time; time.sleep(3000) --config=/etc/myapp/production.yaml --log-level=debug --workers=8--bind=0.0.0.0:8
-l(--long)を付けると切り詰めずに全体を出力する。
$ pstree -pal 425
python3,425 -c import time; time.sleep(3000) --config=/etc/myapp/production.yaml --log-level=debug --workers=8 --bind=0.0.0.0:8080 --metrics-port=9090 --data-dir=/var/lib/myapp
パイプやファイルへ出力する場合は132桁で切り詰められる。grepと組み合わせる場合も-lを付けておく。
コンテナの名前空間を確認する(-S/-N)
-S(--ns-changes)は、親と名前空間が変わる箇所に名前空間の種別を表示する。Dockerホストで実行すると、コンテナの境界が見える。
$ pstree -S
systemd-+-2*[agetty]
|-containerd---11*[{containerd}]
|-containerd-shim-+-systemd(ipc,mnt,net,pid,uts)-+-bash---2*[sleep]
| | |-dbus-daemon
| | |-nginx---4*[nginx]
| | |-rsyslogd(uts)---3*[{rsyslogd}]
| | |-sh---top
| | |-sshd---sshd---sshd---sh---sleep
| | |-systemd---(sd-pam)
| | |-systemd-journal
| | |-systemd-logind(mnt,uts)
| | `-systemd-resolve(mnt)
| `-10*[{containerd-shim}]
|-dbus-daemon
|-dockerd---13*[{dockerd}]
|-systemd-journal
`-systemd-udevd(mnt,uts)
containerd-shimの下のsystemdでipc,mnt,net,pid,utsの名前空間が切り替わっている。ここから先がコンテナの中である。
-N TYPE(--ns-sort)は名前空間ごとにツリーを分けて表示する。pidを指定すると、コンテナごとのプロセスツリーが名前空間のIDとともに並ぶ。
$ pstree -N pid
[4026531836]
systemd-+-2*[agetty]
|-containerd---11*[{containerd}]
|-containerd-shim---10*[{containerd-shim}]
|-dbus-daemon
|-dockerd---13*[{dockerd}]
|-systemd-journal
`-systemd-udevd
[4026532398]
systemd-+-bash---2*[sleep]
|-dbus-daemon
|-nginx---4*[nginx]
|-rsyslogd---3*[{rsyslogd}]
|-sh---top
|-sshd---sshd---sshd---sh---sleep
|-systemd---(sd-pam)
|-systemd-journal
|-systemd-logind
`-systemd-resolve
指定できる種別はcgroup、ipc、mnt、net、pid、time、user、utsである。
psやpgrepとの使い分け
| コマンド | 向いている用途 |
|---|---|
pstree | 親子関係の把握、起動元の特定 |
ps | CPU使用率やメモリ使用量など詳細な情報の確認 |
pgrep | 条件に合うプロセスのPIDだけを取得 |
構造を見る場合はpstree、数値を見る場合はpsを使う。pgrepで見つけたPIDをpstree -psに渡すと、対象のプロセスの素性を素早く調べられる。
$ pstree -ps $(pgrep -x top)
systemd(1)---sh(255)---top(256)
macOSでの利用
macOSにpstreeは標準で入っていない。Homebrewで導入できるが、中身はpsmiscとは別実装のFredHucht/pstree
であり、オプションの体系も異なる。
$ brew install pstree
参考
- pstree(1) - Linux manual page
- psコマンドでプロセスの起動日時を表示する(lstart)
- 特定ファイルをオープンしているプロセスを探す
- 【Docker】コンテナの外から動作しているプロセスを調べる(docker container top)
