LoginSignup
1
1

More than 1 year has passed since last update.

Raspberry Pi4 screenセッションの確認方法

Last updated at Posted at 2022-04-03

序文

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_02.png
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プロセスを生成している、という仕組みであることが分かる。簡単な図に起こすと以下。
screen_07.png

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が1systemd)なので、これでビンゴらしい。または/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/initsystemdのシンボルリンク。起点である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とは、最初に起動するプロセスであり、PID1固定。以下、次々とシステムに必要なサービスが子プロセスとして起動されていく。従ってプロセスの依存関係は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コマンドの結果では、systemdPPID列の値は0。プロセスIDの最小値は1という仕様であり、0は存在しない筈。psコマンドでもプロセスID に0を指定するとエラーになる。

プロセスID に 1 を指定
$ 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
プロセスID に 0 を指定
$ 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).

systemdPPID列の値に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 を考え直す - ぽぽの備忘録

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1