序文
screenコマンド。とても便利で重宝している。
ところが調子に乗って複数の ssh接続で複数の screenセッションを作成し、他の作業と行ったり来たりしていると混乱してくる。
screen -ls
でセッションの一覧は確認できる。
$ screen -ls
There are screens on:
25987.pts-2.raspberrypi4 (2022年04月03日 10時54分18秒) (Attached)
25946.pts-0.raspberrypi4 (2022年04月03日 10時54分10秒) (Attached)
2 Sockets in /run/screen/S-pi.
しかし、このコマンドを実行している自分は screenセッション上なのか、もしそうであれば、一覧のどれなのか。ぱっと見、判断できない。
ターミナルのタイトルにセッション情報を表示する、というアプローチもあるけど、今回はコマンド(ワンライナー)で確認する方法を考えてみる。
環境
サーバのLinux
環境は Raspberry Pi OS bullseye、クライアントは Windows10 Home、ターミナルは TeraTerm。
いわゆるラズパイ環境だけど、Linux汎用と思われ。
以下のエイリアスがデフォルトで定義されているので、明示的に強調表示色の指定は省略。自環境に合わせて読み替えること。
$ alias grep
alias grep='grep --color=auto'
$ alias egrep
alias egrep='egrep --color=auto'
基本的なコマンド
- pstree
実行中のプロセスをツリー形式で表示する。
スイッチ | 意味 |
---|---|
-p | PID を表示する。PID は10進数でプロセス名の後に括弧付で表示される。-p を付けると、ツリーのまとめ表示は行わない |
-s | 選択したプロセスの親を表示する |
-h | カレントプロセスとその先祖のプロセスを強調表示する。ターミナルが強調表示をサポートしていなかったり、カレントプロセスとその上位グループのいずれもがツリーに属していなければ、この指定は単に無効となる |
-U | UTF-8 (Unicode) の罫線文字 (line drawing character) を使う |
- ps
現在実行されているプロセスのスナップショットを表示する。
スイッチ | 意味 |
---|---|
u | ユーザー指向のフォーマット |
l | BSD の長いフォーマット。親PID(PPID)を表示する |
p | プロセス ID で選択する。-p, --pid と等しい |
ax | 全てのプロセス表示 |
ps
コマンドのスイッチはハイフン(-
)の有り無しで意味が変わる場合があるので注意。(というかそれが大半か)
head
コマンドで先頭一行切り出しはhead -1
で良い。head -n 1
より少しシンプルになる。
しかし、この用法、ドキュメントを見ても載っていないんだよなぁ。(自分が見落としている?)
組込変数。
変数 | 意味 |
---|---|
$$ | シェルのプロセスID |
$PPID | 親のプロセスID |
基本的な仕組みの確認
まず自身のシェルプロセスID($$
)、親プロセスID($PPID
)、セッションの一覧(screen -ls
)、ツリー構造(pstree -pshU $$ | egrep "|$$"
)を確認。

egrep
コマンドがプロセスツリー上ではgrep
として表示されている。これは、egrep
の実態がgrep -E
だから。
$ cat /usr/bin/egrep
#!/bin/sh
exec grep -E "$@"
しかしsshd
screen
プロセスが二重(親子?)になっているのがよく分からないなぁ。それはさておき。
コマンドを実行している自プロセス(28129)はbash
であり、screen
(28128)プロセスの子プロセスであることが分かる。
簡単な図に起こすと以下。
screen -ls
で表示されるセッション一覧の形式はpid.tty.host
。
コマンド実行環境(bash)から見て、親(screen
プロセス)のPID
がこの一覧に表示されていることが分かる。
つまり、screen -ls
で親PIDが含まれる行を強調表示すれば良いことになる。コマンド例は以下。
$ screen -ls | egrep "|$PPID[.].*"
実行結果は以下。
おぉ、分かりやすい。
また、screen -ls
から全てのPID
を拾いプロセスツリーを表示するなら以下。
$ screen -ls | grep $'\t' | sed -e "s/\t\([0-9]*\)[.].*/\1/" | xargs -i pstree -phU {} | egrep "|$$"
screenセッション全体と自分の場所が確認でき、これもいい感じ。
また、セッション情報は/var/run/screen/S-<login>
に格納されている。
$ ls /var/run/screen/S-`whoami`/
28066.pts-0.raspberrypi4 28128.pts-2.raspberrypi4
ここからセッションを拾って上記のように一覧ツリー化する方法も考えられる。コマンド例は以下。
$ ls /var/run/screen/S-`whoami`/ | sed s/[.].*//g | xargs -i pstree -phU {} | egrep "|$$"
実行結果は以下。
こちらの方が多少コマンドが短くて済む。また、結果としてscreen
セッションのPID
でソートされている。
複数ウィンドウの場合
一つのscreen
セッション内に二つのウィンドウを作成している場合の実行結果は以下。
つまり、複数のウィンドウを生成しているということは、複数のbash
プロセスを生成している、という仕組みであることが分かる。簡単な図に起こすと以下。
sshdを起点に確認
ssh
接続先のssh
サーバであるsshd
サービスのPID
をまず確認。
$ systemctl status sshd | grep Loaded:
Loaded: loaded (/lib/systemd/system/ssh.service; enabled; vendor preset: enabled)
$ grep ExecStart= /lib/systemd/system/ssh.service
ExecStart=/usr/sbin/sshd -D $SSHD_OPTS
/usr/sbin/sshd -D
として実行されているプロセスらしいので、それを検索。
$ ( ps alx | head -1; ps alx | grep "/usr/sbin/sshd -D" | grep -v grep )
F UID PID PPID PRI NI VSZ RSS WCHAN STAT TTY TIME COMMAND
4 0 577 1 20 0 13644 6924 - Ss ? 0:00 sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups
親PIDが1
(systemd
)なので、これでビンゴらしい。または/var/run/sshd.pid
でも確認できる。
$ cat /var/run/sshd.pid
577
但し、後者は複数のsshd
サービスが起動した場合は最後のものが格納される仕様らしいので注意。
取り合えず、後者を採用したsshd
サービス配下のツリー表示コマンドは以下。
$ pstree -phU `cat /var/run/sshd.pid` | egrep "|$$"
新規でssh接続しての実行結果が以下。
この場合、screen
セッションでの接続を含め、sshd
サービス配下が全て表示されることが分かる。
ところが、screen
セッションをデタッチしてから再アタッチし、screen
セッション上から確認すると以下。
デタッチした時点でscreen
セッションの直接の親のsshd
プロセスは破棄され、systemd
に差し変わる仕様らしい。
うーむ。sshd
プロセスを介していないのにどうやってssh
接続しているんだろう?
と思ったけど、この時点の自分はsshd
を親に持つbash
プロセスであり、screen
セッションを作成する前の接続。
一方、screen
セッションはssh
接続とは独立してサーバーサイドに保持される仕様。
再度screen
セッションにアタッチする場合はscreen
セッションの子のbash
に遷移するイメージ。ということになる。
sshd─bash
<───>
screen─bash
psコマンドで先祖を辿る
ps
コマンドでl
スイッチを指定すると親PID(PPID
列)も表示される。これを利用してカレントのプロセス(screen
セッション上)から先祖を辿るコマンド例が以下。
$ ps l | head -1; pid=$$; while [ $pid -ge 1 ]; do ps hlp $pid; pid=`ps hlp $pid | awk '{print $4}'`; done
F UID PID PPID PRI NI VSZ RSS WCHAN STAT TTY TIME COMMAND
0 1000 32419 32418 20 0 6172 4672 do_wai Ss pts/6 0:00 /bin/bash
1 1000 32418 32417 20 0 5420 2736 do_sel Ss ? 0:00 SCREEN
0 1000 32417 32398 20 0 5128 2876 do_sys S+ pts/5 0:00 screen
0 1000 32398 32397 20 0 6144 4584 do_wai Ss pts/5 0:00 -bash
5 1000 32397 32388 20 0 16064 4768 - S ? 0:00 sshd: pi@pts/5
4 0 32388 577 20 0 16064 7960 - Ss ? 0:00 sshd: pi [priv]
4 0 577 1 20 0 13644 6924 - Ss ? 0:00 sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups
4 0 1 0 20 0 166904 10504 - Ss ? 0:06 /sbin/init
/sbin/init
はsystemd
のシンボルリンク。起点であるsystemd
に辿りついていることが分かる。
$ ls -la /sbin/init
lrwxrwxrwx 1 root root 20 3月 21 04:55 /sbin/init -> /lib/systemd/systemd
一旦デタッチしてから再度アタッチ。上記コマンドを再実行した結果が以下。やはりscreen
プロセスの親がsystemd
に差し変わっていることが分かる。
$ ps l | head -1; pid=$$; while [ $pid -ge 1 ]; do ps hlp $pid; pid=`ps hlp $pid | awk '{print $4}'`; done
F UID PID PPID PRI NI VSZ RSS WCHAN STAT TTY TIME COMMAND
0 1000 32419 32418 20 0 6172 4672 do_wai Ss pts/6 0:00 /bin/bash
1 1000 32418 1 20 0 5420 2748 do_sel Ss ? 0:00 SCREEN
4 0 1 0 20 0 166904 10504 - Ss ? 0:06 /sbin/init
pstree
コマンドでも先祖を辿ることができるけど、こちらの方法は詳細が表示されるので、これはこれでありかも。
それはそうと、気になったのは最後のsystemd
の行。PPID
列の値が0
。
systemd
とは、最初に起動するプロセスであり、PID
は1
固定。以下、次々とシステムに必要なサービスが子プロセスとして起動されていく。従ってプロセスの依存関係はsystemd
を起点とした樹形図構造であり、全てのプロセスの親を辿っていくと必ずsystemd
に辿り着く。というのが基本的な仕様。つまり、systemd
の親は存在しない。
クリックで開閉
$ pstree
systemd─┬─apache2───5*[apache2]
├─applet.py
├─avahi-daemon───avahi-daemon
├─bluetoothd
├─colord───2*[{colord}]
├─cron
├─cups-browsed───2*[{cups-browsed}]
├─cupsd───dbus
├─2*[dbus-daemon]
├─dhcpcd
├─fcitx───mozc_server───2*[{mozc_server}]
├─fcitx-dbus-watc
├─hciattach
├─lightdm─┬─Xorg───{Xorg}
│ ├─lightdm─┬─lxsession─┬─lxpanel───4*[{lxpanel}]
│ │ │ ├─lxpolkit───2*[{lxpolkit}]
│ │ │ ├─openbox
│ │ │ ├─pcmanfm───2*[{pcmanfm}]
│ │ │ ├─ssh-agent
│ │ │ └─2*[{lxsession}]
│ │ └─2*[{lightdm}]
│ └─2*[{lightdm}]
├─login───bash
├─menu-cached───2*[{menu-cached}]
├─packagekitd───2*[{packagekitd}]
├─polkitd───2*[{polkitd}]
├─rngd───3*[{rngd}]
├─rpc.statd
├─rpcbind
├─rsyslogd───3*[{rsyslogd}]
├─rtkit-daemon───2*[{rtkit-daemon}]
├─screen───2*[bash]
├─2*[screen───bash]
├─ssh-agent
├─sshd─┬─sshd───sshd───bash
│ ├─2*[sshd───sshd───bash───screen]
│ └─sshd───sshd───bash───pstree
├─systemd─┬─(sd-pam)
│ ├─dbus-daemon
│ ├─gvfs-afc-volume───3*[{gvfs-afc-volume}]
│ ├─gvfs-goa-volume───2*[{gvfs-goa-volume}]
│ ├─gvfs-gphoto2-vo───2*[{gvfs-gphoto2-vo}]
│ ├─gvfs-mtp-volume───2*[{gvfs-mtp-volume}]
│ ├─gvfs-udisks2-vo───3*[{gvfs-udisks2-vo}]
│ ├─gvfsd─┬─gvfsd-trash───2*[{gvfsd-trash}]
│ │ └─2*[{gvfsd}]
│ ├─gvfsd-fuse───5*[{gvfsd-fuse}]
│ ├─gvfsd-metadata───2*[{gvfsd-metadata}]
│ ├─pipewire─┬─pipewire-media-───{pipewire-media-}
│ │ └─{pipewire}
│ └─pulseaudio───2*[{pulseaudio}]
├─systemd-journal
├─systemd-logind
├─systemd-timesyn───{systemd-timesyn}
├─systemd-udevd
├─thd
├─udisksd───4*[{udisksd}]
├─vncagent
├─vncserver-x11-s───vncserver-x11-c
├─vncserverui───vncserverui
├─watchdog
├─2*[wpa_supplicant]
└─xcompmgr
ところが前述のps
コマンドの結果では、systemd
のPPID
列の値は0
。プロセスIDの最小値は1
という仕様であり、0
は存在しない筈。ps
コマンドでもプロセスID に0
を指定するとエラーになる。
$ ps lp 1
F UID PID PPID PRI NI VSZ RSS WCHAN STAT TTY TIME COMMAND
4 0 1 0 20 0 166904 10504 - Ss ? 0:06 /sbin/init
$ ps lp 0
error: process ID out of range
Usage:
ps [options]
Try 'ps --help <simple|list|output|threads|misc|all>'
or 'ps --help <s|l|o|t|m|a>'
for additional help text.
For more details see ps(1).
systemd
のPPID
列の値に0
が表示されるのは、存在しないことを便宜的に表しているのかな? と思っていたら、以下が参考になった。
LinuxおよびSolarisでのPIDの範囲はどのくらいですか?
最小PIDは実際には0です。つまり、実際にはプロセスではないカーネルです...
カーネルはPIDを取得しますか?
必要はありませんが、pid 0が与えられます。LinuxではPPIDとしてのみ表示されると思いますが、Solarisでは間違いなく/ proc / 0エントリがあり、psはプロセス0を「スケジュール済み」として報告します。
へーへーへー。
まとめ(コピペ用)
コマンド | 意味 |
---|---|
echo $$ $PPID | 自プロセス、親プロセスID 表示 |
ps lp $$ | 自プロセス表示(ps コマンド`) |
pstree -pshU $$ | egrep "|$$" |
自プロセスを含むツリー表示 |
ps l | head -1; pid=$$; while [ $pid -ge 1 ]; do ps hlp $pid; pid=`ps hlp $pid | awk '{print $4}'`; done | 自プロセスを起点に先祖表示(ps コマンド版) |
screen -ls |
screen セッション一覧表示 |
screen -ls | egrep "|$PPID[.].*" |
screen セッション一覧表示(自プロセス強調表示版) |
screen -ls | grep $ '\t' | sed -e "s/\t\([0-9]*\)[.].*/\1/" | xargs -i pstree -phU {} | egrep "|$$" |
screen セッション一覧ツリー表示(screen -ls 版) |
ls /var/run/screen/S-`whoami`/ | sed s/[.].*//g | xargs -i pstree -phU {} | egrep "|$$" |
screen セッション一覧ツリー表示(プロセスファイル版) |
pstree -phU `cat /var/run/sshd.pid` | egrep "|$$" |
sshd サービス配下のツリー表示 |
参考URL
pstree(1) — manpages-ja — Debian bullseye — Debian Manpages
ps(1) — manpages-ja — Debian bullseye — Debian Manpages
screen(1) — manpages-ja — Debian bullseye — Debian Manpages
#きょうのsystemd_ (翻訳)PID 1 を考え直す - ぽぽの備忘録