psコマンドのSZって何だ?

2021年6月14日

psコマンドは誰もがお世話になる便利コマンド。派生コマンドも多いし、リアルタイムに見たいからtop使ったり、その派生使ったり、アウトローな輩が/procを直に見るからpsコマンドいらんとか言ってたり、sar経由でグラフにならないと見ない御仁もいるかもしれないけど、とにかくその誰もが最初に学ぶ基本コマンドがpsなのだ。

今日はそのpsコマンドの出力、SZについての疑問に迫る。うん、誰も興味ないのは知ってる。

psコマンドとは ~ おさらい ~

誰もが知ってると言われても俺は知らないという未開の地からの来訪者のために、wikipediaの説明を贈ろう。これを故郷で自慢してほしい。

嘘です。やる気のない人が書いたのか意味のある語句は、

ps(ピーエス)は現在動作しているプロセスを表示する …

https://ja.wikipedia.org/wiki/Ps_(UNIX)

これだけでしたね。プロセスにも何かリンクが張ってるので、良ければそこも読んでくれて構いませんが、動いてる状態のプログラムをプロセスって言います。だからpsコマンドは自分自身のプロセスも、その一覧に表示します。例えばこんな感じ。

$ ps
    PID TTY          TIME CMD
  40933 pts/4    00:00:00 bash
  93843 pts/4    00:00:00 ps
$ 

今はbashとpsというプロセスが動いてるのが分かります。

psはコマンドなので、オプションを変えることにより出力が変わります。例えばオプション-fを付けると…

$ ps -f
UID          PID    PPID  C STIME TTY          TIME CMD
user       93862    1991  1 12:28 pts/6    00:00:00 /bin/bash
user       94063   93862  0 12:28 pts/6    00:00:00 ps -f
$

情報が増えました。よく使うオプションは-efとかです。

$ ps -ef
UID          PID    PPID  C STIME TTY          TIME CMD
root           1       0  0  6月04 ?      00:00:04 /sbin/init splash
root           2       0  0  6月04 ?      00:00:00 [kthreadd]
root           3       2  0  6月04 ?      00:00:00 [rcu_gp]
root           4       2  0  6月04 ?      00:00:00 [rcu_par_gp]
root           6       2  0  6月04 ?      00:00:00 [kworker/0:0H-kblockd]
root           9       2  0  6月04 ?      00:00:00 [mm_percpu_wq]
root          10       2  0  6月04 ?      00:00:18 [ksoftirqd/0]
root          11       2  0  6月04 ?      00:00:52 [rcu_sched]
root          12       2  0  6月04 ?      00:00:01 [migration/0]
...
$

今度は一覧されるプロセスが増えました。まあこんなコマンドです。

ではSZって何?

ps -elyで出てくる列の1つです。

$ ps -ely
S   UID     PID    PPID  C PRI  NI   RSS    SZ WCHAN  TTY          TIME CMD
S     0       1       0  0  80   0  7828 41936 -      ?        00:00:04 systemd
S     0       2       0  0  80   0     0     0 -      ?        00:00:00 kthreadd
I     0       3       2  0  60 -20     0     0 -      ?        00:00:00 rcu_gp
I     0       4       2  0  60 -20     0     0 -      ?        00:00:00 rcu_par_gp
I     0       6       2  0  60 -20     0     0 -      ?        00:00:00 kworker/0:0H-kblockd
I     0       9       2  0  60 -20     0     0 -      ?        00:00:00 mm_percpu_wq
S     0      10       2  0  80   0     0     0 -      ?        00:00:18 ksoftirqd/0
I     0      11       2  0  80   0     0     0 -      ?        00:00:52 rcu_sched
S     0      12       2  0 -40   -     0     0 -      ?        00:00:01 migration/0
...
$ 

psコマンドのオプションは、実は標準とBSD系列に分かれており、

  • ps -efを典型例とする標準オプション
  • ps auxを典型例とするBSD系列オプション

に2分されますが、標準オプションではメモリ使用量を知る簡単なオプションが-yしかないのです(長ったらしいフォーマット指定をすれば任意の列を出力出来ますが…)。

そしてこの-yオプションで出てくるメモリ関連の列、RSSとSZのうち、RSSは

… resident set size, the non-swapped physical memory that a task has used (in kilobytes). …

Manual page ps(1)

と分かりやすく、プロセスが使っているスワップされてない物理メモリの常駐サイズ[KB]です。ザックリとこのプロセス単独で使ってるRAM使用量くらいの意味合いです。

でもSZは…

… size in physical pages of the core image of the process. This includes text, data, and stack space. Device mappings are currently excluded; this is subject to change. See vsz and rss. …

Manual page ps(1)

よく分からない…物理メモリだし普通のセクション名だし、デバイスマッピングを除くんだからRSSと似たようなものなんだろうか…でもなぜ?何が違うんだろう?

これが最初の所感でした。何とも言いようのない違和感を覚えるものの、今どきBSD系列もおかしいし、標準で行きたい子羊は-yを使いたい一心でSZ探しの旅に出るのでした。

SZ探しの旅

何をするって?もうソースを探すしかないんですよ…

psコマンドを含むパッケージを探して…

我らが住む大地Ubuntuを構成するパッケージのうちどこにpsコマンドが住んでいるのかをまず調べます。

ローカルでコマンドを使用する方法(オススメしません)

$ sudo apt install apt-file
$ apt-file update
$ apt-file search -x '^/bin/ps$'
procps: /bin/ps
$

Webでサクッと探す方法(オススメ)

https://packages.ubuntu.com/ja/

パッケージの内容を検索で、psで検索するだけでprocpsが見つかります。

procpsのソースパッケージを取得する

まずはソースを取得するリポジトリをaptに設定する必要があります。たくさんやると時間かかるので、ピンポイントで取得するため、事前にどこにあるのか調べます。

$ apt info procps
...
APT-Sources: http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages
...
$

見つかったので、上のリポジトリを/etc/apt/source.listで探してコメントを外します。

< # deb-src http://archive.ubuntu.com/ubuntu/ focal-updates main restricted
-- 
> deb-src http://archive.ubuntu.com/ubuntu/ focal-updates main restricted

では更新してソース取得します。ついでにビルドに必要なパッケージも追加しておきます。

$ sudo apt update
$ sudo apt install build-essential dpkg-dev
$ apt source procps

とりあえずこの段階でソースを見ることが出来ます。

ビルドするにはビルドで依存するパッケージも必要なので、それらを入れておく必要があります。

$ sudo apt build-dep procps

あとはビルドディレクトリに行って、ビルドするだけ。

$ cd procps-3.3.16 # バージョン部分は適宜読み替えてください
$ dpkg-buildpackage -rfakeroot -b

一度ビルドが通ったら、デバッグしたくて最適化がうざそうなので、Makefileを直に編集して最適化を外します。

< CFLAGS = -g -O2 -fdebug-prefix-map=/root/ps/procps-3.3.16=. -fstack-protector-strong -Wformat -Werror=format-security
--
> CFLAGS = -g -fdebug-prefix-map=/root/ps/procps-3.3.16=. -fstack-protector-strong -Wformat -Werror=format-security

改めてリビルド。

$ make clean
$ make

後は思う存分デバッグして解析するだけです。

gdb神の力を借りる

実はこの作業dockerコンテナ内でやってるので、gdbをインストールして直に使います。

$ sudo apt install gdb
$ gdb ps/.libs/pscommand
(gdb) break main
(gdb) break pr_vsz
(gdb) break pr_rss
(gdb) break pr_sz
(gdb) run -eo vsz,rss,sz
Starting program: /root/ps/procps-3.3.16/ps/.libs/pscommand -eo vsz,rss,sz
warning: Error disabling address space randomization: Operation not permitted
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Breakpoint 1, main (argc=1, argv=0x0) at ps/display.c:621
621     int main(int argc, char *argv[]){
(gdb) c
Continuing.
   VSZ   RSS    SZ

Breakpoint 2, pr_vsz (outbuf=0x7f6f54a79090 "SZ\n", pp=0x200000001) at ps/output.c:584
584     static int pr_vsz(char *restrict const outbuf, const proc_t *restrict const pp){
(gdb) n
585       return snprintf(outbuf, COLWID, "%lu", pp->vm_size);
(gdb) c
Continuing.

Breakpoint 3, pr_rss (outbuf=0x0, pp=0x7ffcaa6bac10) at ps/output.c:990
990     static int pr_rss(char *restrict const outbuf, const proc_t *restrict const pp){
(gdb) n
991       return snprintf(outbuf, COLWID, "%lu", pp->vm_rss);
(gdb) c
Continuing.

Breakpoint 4, pr_sz (outbuf=0x0, pp=0x7ffcaa6bac10) at ps/output.c:898
898     static int pr_sz(char *restrict const outbuf, const proc_t *restrict const pp){
(gdb) n
899       return snprintf(outbuf, COLWID, "%lu", (pp->vm_size)/(page_size/1024));
(gdb) 

はい、これで確認が取れて全て把握です。proc/readproc.cも読むと、/proc/[pid]/statusを読んで、VmSizeやらVmRSSを拾ってる模様。

列名
RSSpp->vm_rss/proc/[pid]/statusのVmRSS
VSZpp->vm_size/proc/[pid]/statusのVmSize
SZ(pp->vm_size)/(page_size/1024)VSZの値を4で割ったモノ(page_sizeは4kなので)

あとはman 5 procしても良いし、

https://www.kernel.org/doc/html/latest/filesystems/proc.html

を見てもOK。

SZとは

SZとは仮想メモリ含む使用メモリ(スワップしてるメモリも含んだ全使用メモリ)のページ数でした(※man psを信用してはならない!?)。
4掛けると仮想メモリ含む全使用メモリ[KB]になります。

これでBSDの呪いに屈することなく、-elyを思う存分使える!ということ。ん?4掛けるのが面倒?仕様です。

一応続き的な記事を書きました。