ps aux の出力を1列ずつ解剖

障害対応でpsを打っても数字の意味が曖昧なままでは切り分けが進まない。ps auxの11列を1列ずつ精読し、%CPUが平均値である理由、VSZとRSSの差、D状態の意味まで現場で効く読み方を身につける。

応用コマンド出力Linuxプロセス運用最終更新: 2026-07-28
3つの要点
TL;DR
  1. ps auxは11列でプロセスを示す。%CPUは現在値ではなく、起動後の累積CPU時間を経過時間で割った平均だ。今この瞬間の負荷を調べるならtopを使う。
  2. VSZは未使用領域や共有ライブラリも含む仮想アドレス空間の総量。RSSは物理メモリ上の量だが、共有部分を各プロセスへ重複計上するため、RSSの全件合計は実使用量にならない。
  3. STATの先頭は状態で、Rは実行可能、Sは割り込み可能待ち、DはI/Oなどの割り込み不可待ち、Zはゾンビ、Tは停止。D状態はkill -9でも消えず、ディスクやネットワークの応答側を調べる。

このコマンドは何を見せているか

psが見せているのは、コマンドを打った瞬間のプロセステーブルの写真だ。topのような更新され続ける計器ではない。実体はカーネルが/proc配下に置く各PIDのディレクトリで、psはその擬似ファイルを一巡して読んで並べるだけだ。この「1回読んで終わり」が%CPUの解釈を左右する。

auxにハイフンが付かないのも誤記ではない。psにはハイフンなしのBSD形式と、ハイフン付きのUNIX(System V)形式という2つの流儀がある。auxはBSD形式のオプション3つの組で、aは自分以外のユーザーのプロセスも含める、uは詳細な列構成にする、xは制御端末を持たないプロセスも含める。aとxで初めて「全プロセス」になり、uが以下の11列を決める。

ハイフン1文字で別のコマンドになる

ps auxps -aux は本来別物だ。後者はUNIX形式と解釈され、-uがユーザー指定のオプションになるため、xという名前のユーザーのプロセスを表示せよという意味になる。procps-ngはxが存在しないときだけ警告付きでauxと解釈する。

プロセスやメモリ管理の仕組み自体はOSの話題であり、本稿は読解に集中する。

横にスクロール

ps auxのCPUメモリ状態列から対象プロセスを特定し親子関係まで調べる手順
累積CPU時間だけで重さを断定せず、瞬間値・開始時刻・STAT・親子関係を合わせて判断する。

1列ずつ解剖

題材は、アプリケーションサーバー1台で ps aux を打った出力の抜粋だ。現在時刻は7月16日14時33分とする。

USER       PID %CPU %MEM     VSZ    RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.3  168404  12996 ?        Ss   Jul02   3:41 /sbin/init
root         2  0.0  0.0       0      0 ?        S    Jul02   0:00 [kthreadd]
root        14  0.0  0.0       0      0 ?        I<   Jul02   0:00 [kworker/0:0H-events_highpri]
root       412  0.0  0.0       0      0 ?        S    Jul02   0:53 [jbd2/nvme0n1p2-8]
root       688  0.0  0.3   96820  15612 ?        Ss   Jul02   0:22 /usr/sbin/sshd -D
www-data   902  0.4  0.9  214880  38112 ?        S    Jul02  84:12 nginx: worker process
deploy    2148 78.6 23.6 4198520 992304 ?        Sl   09:41 229:31 java -Xmx2g -jar app.jar
deploy    2210  0.1  0.0       0      0 ?        Z    14:02   0:02 [convert] <defunct>
deploy    2884  0.4  0.2   28560   9884 ?        D    14:28   0:01 rsync -a /mnt/nfs/backup/ .
deploy    3055  0.0  0.1   17092   5240 pts/0    Ss   14:02   0:00 -bash
deploy    3120  0.0  0.0   21536   3204 pts/0    T    14:05   0:00 vim deploy.log
deploy    3390 12.0  0.6  246220  25640 ?        RN   14:31   0:14 python3 /opt/batch/aggregate.py
deploy    3402  0.0  0.0   15008   2100 pts/0    R+   14:33   0:00 ps aux

USER — どの権限で動いているか

先頭は実効ユーザーIDを名前に解決した値だ。実効ユーザーとは、そのプロセスが今どの権限でファイルやシステムコールへアクセスできるかを決める主体で、起動した本人(実ユーザー)と一致するとは限らない。setuidやsudo経由では両者がずれる。

PID — 一意だが永久ではない

プロセスID。1はinit(今日ではsystemdが多い)、2はkthreaddで、カーネルスレッドはすべて2の子孫だ。PIDは/proc/sys/kernel/pid_maxまで昇順に払い出され、上限で小さい値へ折り返して再利用される。「今は一意」でも「永久に一意」ではない。数分前にメモしたPIDへkillを撃つのは、同じ番号が別物へ再配布されている危険を伴う。

%CPU — 現在値ではなく起動からの平均

ここが最大の落とし穴だ。%CPUは、そのプロセスが起動してから今までに消費したCPU時間を、同じ区間の経過時間で割った値にすぎない。javaの78.6は目を引くが、これは「今78.6パーセント使っている」ではなく「09:41の起動から約4時間52分、ならして78.6パーセント使ってきた」という意味だ。逆に、たった今100パーセントへ張り付いたプロセスでも、起動が2週間前なら%CPUは0.0のままである。なおSMP環境では複数スレッドが並列に回れば100を超える。

%MEM — RSSを総物理メモリで割っただけ

%MEMは後述のRSSを総物理メモリ(/proc/meminfoのMemTotal)で割った百分率だ。javaはRSSが992304、%MEMが23.6なので、この環境の物理メモリはおよそ4GiBと逆算できる。RSS由来なので、RSSが抱える問題をそのまま引き継ぐ。スワップへ追い出された分は含まれない。

VSZ と RSS — 宣言と実体

この2列は単位がどちらもKBで隣り合うが、意味はほとんど正反対だ。

何を表すか含まれるもの落とし穴
VSZ仮想アドレス空間の総量(KB)未使用の予約領域、mmapしたファイル、共有ライブラリの全マップ物理メモリ消費と直結しない。大きくても異常とは限らない
RSS物理メモリに載っているページの合計(KB)実際に触ったページ、常駐している共有ライブラリ共有ページを全プロセスへ重複計上。合計しても実使用量にならない

VSZ(Virtual Size)は仮想アドレス空間の総量だ。確保しただけで一度も書き込んでいない領域、共有ライブラリの全マップ、JVMが予約したヒープ上限まで、「使うかもしれないと宣言した空間」がすべて入る。javaがVSZ約4.2GBに対しRSS約970MBなのは異常ではなく、-Xmx2gの予約が仮想空間だけを膨らませている姿だ。VSZを根拠にメモリリークと騒ぐのは、ほぼ誤診である。

RSS(Resident Set Size)は、そのうち現に物理メモリへ載っているページの合計だ。実体を映すが、致命的な癖については後述する。

TTY — 端末にぶら下がっているか

制御端末の名前だ。pts/0はSSHや端末エミュレータ越しの擬似端末、tty1は物理コンソール、疑問符はデーモンやカーネルスレッドのように制御端末を持たないものを示す。端末が閉じたときSIGHUPで巻き添えを食うのは端末を持つ側だ。

STAT — 状態1文字と付加文字

1文字目がプロセスの状態を、2文字目以降が付加情報を表す。障害対応で最初に見るべき列だ。

文字状態現場での意味
R実行中または実行可能CPUを使っているか、CPU待ちの列に並んでいる
S割り込み可能スリープイベント待ちの正常な待機。大半のプロセスはこれ
D割り込み不可スリープI/O完了待ち。シグナルが届かない。続くならストレージを疑う
Zゾンビ(defunct)終了済みで親のwait待ち。資源は解放済み
T停止中SIGSTOPやCtrl+Zによる停止。デバッガ由来の停止は小文字のt
Iアイドル仕事のないカーネルスレッド。ロードアベレージには数えない

付加文字は状態の右に続く。

付加文字意味サンプルでの例
sセッションリーダーSs の init・sshd・bash
lマルチスレッドSl の java
+フォアグラウンドプロセスグループに属するR+ の ps aux 自身
N低優先度(nice値が正)RN の python3
<高優先度(nice値が負)I< の kworker
Lメモリ内にロックされたページを持つサンプルには無し

サンプルを読み直そう。init・sshd・bashは Ss でセッションリーダー、javaは Sl で複数スレッド、python3は RN でnice値を上げて(優先度を下げて)実行中、ps aux 自身は R+ でフォアグラウンドだ。kworkerは待機中のIに高優先度の記号が添えられた形になる。

START と TIME — 起動時刻と累積CPU時間

STARTは起動時刻だ。当日ならHH:MM、それより前ならMmmDD、さらに古ければ年が出る。

TIMEは累積CPU時間(ユーザー時間とシステム時間の和)を分:秒で表す。経過時間ではない。nginxワーカーは%CPUが0.4と地味だが、7月2日から生き続けてTIMEは84分12秒まで積み上がっている。一方rsyncのTIMEは1秒だ。この3列は次の関係で結ばれる。

%CPU ≒ TIME ÷ (現在時刻 − START)

javaで確かめる。TIME 229分31秒は13771秒、経過は17520秒。13771 ÷ 17520 = 0.786、つまり78.6だ。

COMMAND — 角括弧はカーネルスレッド

uオプションのCOMMANDは、プロセス自身が持つコマンドライン(/proc配下のcmdline)だ。だから java -Xmx2g -jar app.jar のように引数まで読める。ただしプロセスが書き換えられる領域でもあり、nginx: worker process は実行ファイル名ではなくnginxが上書きした文字列だ。

角括弧付きの [kthreadd][jbd2/nvme0n1p2-8] はカーネルスレッドである。ユーザー空間のアドレス空間を持たずcmdlineが空なので、psは代わりにカーネルが保持する短い名前を角括弧で括る。VSZとRSSが揃って0なのも同じ理由だ。ゾンビもcmdlineを失うため角括弧表示になり、末尾にdefunctが添えられる。

aux と -ef — BSD形式とUNIX形式

もう一つの定番が ps -ef だ。-eは全プロセス、-fは詳細表示を指す。

観点ps aux(BSD形式)ps -ef(UNIX形式)
列の構成USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMANDUID PID PPID C STIME TTY TIME CMD
メモリ%MEM・VSZ・RSSが出る出ない
CPU%CPU(起動からの平均)と累積TIMEC(整数の利用率)と累積TIME
親子関係PPIDが出ないPPIDが出る。ゾンビの親を追える
状態STATが出る出ない
得意な場面リソースを食っている犯人の特定プロセスの親子関係の追跡

決定的な違いはPPID(親プロセスID)の有無で、ゾンビの親を突き止めたいときauxでは足りない。サンプルの2210なら ps -efps -o pid,ppid,stat,comm で親を特定し、その親を直す。結局は ps -eo pid,pcpu,rss,stat,comm --sort=-rss のように-oで組み立てるのが速い。

つまずきやすい点

第一に、%CPUを現在の負荷と読むこと。これは起動からの平均で、数分単位の変動はならされている。「今どれが重いか」なら、一定間隔でCPU時間の差分を取るtopコマンドの読み方へ切り替えるしかない。psで代用するなら、間を空けて2回実行しTIMEの差を取る。

第二に、RSSを足し算すること。RSSはlibcのような共有ライブラリのページを、マップしている全プロセスへ丸ごと重複計上する。fork直後の子なら、まだ1バイトも複製していないページまで親子双方に載る。だから全プロセスのRSSの合計は実使用量を大きく上回る。按分したければ、共有ページを共有プロセス数で割るPSS(Proportional Set Size)を/proc配下のsmaps_rollupから読む。

第三に、ゾンビを恐れすぎること。ゾンビはすでに終了してメモリもファイルも解放済みで、終了ステータスを親へ渡すためだけにプロセステーブルの1エントリとして残る抜け殻だ。CPUもメモリも消費しない。2210でVSZとRSSが0なのはそのためだ。killを撃っても意味はない。すでに死んでいるからだ。悪いのは常に親側で、waitを呼んでいないコードが原因だ。親を終了させればPID 1に引き取られて消える。ただし放置してPIDを食い潰せばforkが失敗する。

第四に、D状態へkillを撃ち続けること。Dは割り込み不可のスリープで、多くはディスクやNFSのI/O完了待ちだ。シグナルを確認するコードパスにいないため、SIGKILL(kill -9)ですら届かない。シグナルは保留のまま積まれ、I/Oが完了して初めて配送される。止められないのではなく順番が来ていないだけだ。やるべきなのはプロセスを殴ることではなく、なぜI/Oが返らないのかを調べること、つまりNFSサーバーの無応答やディスクの故障を疑うことである。D状態が並びロードアベレージだけ跳ね上がるのに%CPUは低い、という症状がその典型である。

D状態が消えないときの最初の一手

ps -eo pid,stat,wchan:20,comm のwchan列には、そのプロセスがカーネル内のどの関数で眠っているかが出る。nfsやioで始まる関数名が並んでいれば、原因はプロセス側ではなくストレージ側だと即断できる。

まとめ

ps aux の11列は、権限・識別・負荷・メモリ・所属・状態・正体のかたまりとして読める。押さえる原理は3つだ。%CPUは起動からの平均であって現在値ではない。VSZは宣言、RSSは実体、ただしRSSは共有分を重複して数える。STATの1文字目は次の一手を決める最短の手掛かりになる。psが写すのは1枚の写真にすぎず、その境界さえ分かればあとは-oでの列指定で補える。他のコマンドの読み方はコマンド出力の読み方に揃えてある。

コマンド出力の読み方の記事ガイド

ps aux の出力を1列ずつ解剖を実務で読む

TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。

解決すること

コマンド出力

比較で見る軸

難易度: advanced / カテゴリ: コマンド出力の読み方 / タグ数: 4

導入後に効く点

VSZは未使用領域や共有ライブラリも含む仮想アドレス空間の総量。RSSは物理メモリ上の量だが、共有部分を各プロセスへ重複計上するため、RSSの全件合計は実使用量にならない。

先に潰すリスク

用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。

数字・仕様の読み方
難易度
advanced
カテゴリ
コマンド出力の読み方
タグ数
4

判断チェックリスト

  • 自社の用途が「コマンド出力 / Linux」に近いか確認する。
  • 強みである「ps auxは11列でプロセスを示す。%CPUは現在値ではなく、起動後の累積CPU時間を経過時間で割った平均だ。今この瞬間の負荷を調べるならtopを使う。」が本当に評価軸になるか確認する。
  • 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
  • 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
  • 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
  • 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。

次に確認する観点

コマンド出力Linuxプロセス運用