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---BAがBの親プロセス
A-+-BAに複数の子があり、続く行に残りの子が並ぶ
2*[sleep]同じ形の枝が2つあるためまとめた
{rsyslogd}プロセスではなくスレッド

上の例ではsshd---sshd---sshd---sh---sleepの並びから、SSHでログインしたセッションがsleepを起動したと読み取れる。

インストール

pstreepsmiscパッケージに含まれる。

$ 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段目のsshdaliceに切り替わり、そこから先が利用者の権限で動いている。デーモンが意図した権限まで落ちているかの確認に使える。

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のサービスから起動されたプロセスであれば、祖先にcronsystemdが現れる。

指定したプロセスに子がある場合、祖先に加えて配下のツリーもあわせて表示される。

$ 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が未設定でもエラーにならず、強調のない出力になる。-HTERMが未設定なら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の下のsystemdipc,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

指定できる種別はcgroupipcmntnetpidtimeuserutsである。

psやpgrepとの使い分け

コマンド向いている用途
pstree親子関係の把握、起動元の特定
psCPU使用率やメモリ使用量など詳細な情報の確認
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

参考