top の出力を1行ずつ解剖
サーバーが重いとき、topのどこを見れば原因に辿り着けるのか。ロードアベレージからwa・st、VIRTとRESの違いまで1行ずつ分解し、「CPUが原因か」を即断できる読み方を身につける。
- ロードアベレージは1・5・15分の指数移動平均で、実行可能な処理と割り込み不可のI/O待ちを数える。コア数で割ると混雑度になり、8コアなら8.0がおおよそ満杯だ。
- %Cpu(s)のusはユーザー空間、syはカーネル、waはI/O待ち、stは仮想化ホストに奪われた時間。wa高ならストレージ、st高なら同居VMやクレジット枯渇を疑う。
- VIRTは確保済みの仮想空間、RESは実際に載る物理メモリだ。%CPUは1コアを100%とするIrix方式なので、マルチスレッド処理は100を超え、全体ではコア数×100まで伸びる。
このコマンドは何を見せているか
topは、稼働中のシステムを一定間隔でサンプリングし、全体の負荷とプロセスごとの資源消費を1画面に見せるコマンドだ。既定では3秒ごとに情報を読み直し、%CPUの降順に並べ替えて再描画する。
画面は上下2部に分かれる。上部のサマリ領域はヘッダ行・Tasks行・%Cpu(s)行・メモリ2行の計5行でシステム全体を要約し、下部のプロセス表は、その瞬間に誰が何をどれだけ食っているかを1行1プロセスで並べる。
ここで最初に押さえるべきは、上下で時間の意味が違う点だ。ロードアベレージは過去1分・5分・15分の平均、%Cpu(s)と%CPUは前回の描画から今回までの区間の増分、TIME+はプロセスが生まれてからの累積である。同じ画面に平均・瞬間・累積という3つの時間軸が同居しており、これを意識せず数字を突き合わせると辻褄が合わなくなる。
topが独自に計測しているものは何もない。カーネルが /proc/stat や /proc/loadavg、/proc/PID/stat に公開する数値を読んで差分を取り、整形しているだけだ。各項目の意味はtopではなくカーネル側の定義で決まる。
横にスクロール
1行ずつ解剖
題材は、8コアの仮想マシンでJavaのアプリケーションとPostgreSQLが同居している環境の出力だ(プロセス表は上位のみ抜粋)。
top - 14:32:07 up 12 days, 3:21, 2 users, load average: 3.42, 2.88, 2.31
Tasks: 213 total, 2 running, 210 sleeping, 0 stopped, 1 zombie
%Cpu(s): 21.7 us, 4.3 sy, 0.0 ni, 62.5 id, 10.8 wa, 0.0 hi, 0.5 si, 0.2 st
MiB Mem : 15884.3 total, 412.8 free, 8930.1 used, 6541.4 buff/cache
MiB Swap: 4096.0 total, 3968.0 free, 128.0 used. 6402.7 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
2291 app 20 0 4821516 981234 32768 R 187.3 6.0 142:07.88 java
1834 postgres 20 0 2476380 524288 49152 S 24.6 3.2 18:42.11 postgres
412 root 20 0 0 0 0 D 6.2 0.0 3:12.40 kworker/u16:2
1102 www-data 20 0 212456 18432 10240 S 1.3 0.1 0:44.19 nginx
3077 app 30 10 198340 9216 5120 S 0.3 0.1 0:02.15 backup.sh
1 root 20 0 168420 12844 8192 S 0.0 0.1 0:09.77 systemd
ヘッダ行 — uptime とロードアベレージ
1行目はuptimeコマンドの出力そのものだ。現在時刻、upに続く連続稼働時間(12日と3時間21分)、ログイン中のユーザー数、そしてロードアベレージが並ぶ。
3つの数字は過去1分・5分・15分に対応する指数移動平均で、単純平均ではなく古い値ほど指数関数的に重みが減る。カーネルは5秒ごとにサンプリングし、実行可能状態(R)にあるプロセスと、割り込み不可能な待ち状態(D)に入ったプロセスの数を数えて平均へ流し込む。
3つ並んでいる意味は、傾きが読めることにある。サンプルの 3.42, 2.88, 2.31 は直近1分ほど大きく、負荷が上昇中だと分かる。逆に 2.31, 2.88, 3.42 の並びなら山を越えて沈静化に向かっている。1つの数字ではなく並びの向きを見る。
Tasks行 — プロセスの数と状態の内訳
totalはカーネルが把握している全プロセス(スレッドではない)の数で、内訳がrunning・sleeping・stopped・zombieに分かれる。
ここに罠がある。この4分類にD状態の枠が無い。topはR・T・Z以外をすべてsleepingに数えるため、ディスクI/Oで固まっているD状態のプロセスもsleepingに紛れ込む。「sleepingが210もあるから暇だ」とは読めない。ロードアベレージはDを数えるのにTasks行はDを見せない、というねじれがこの行の本質だ。
zombieは、終了済みだが親プロセスが終了ステータスを回収していないプロセスだ。プロセステーブルのエントリだけが残り、CPUもメモリも消費しない。1つ2つなら実害は薄いが、増え続けるなら親の実装かハングを疑う。
%Cpu(s)行 — CPU時間が何に使われたか
この行は、前回の描画から今回までの区間でCPU時間がどこへ流れたかを、全コアの平均を100として配分する。
| 略号 | 意味 | 高いときに読むこと |
|---|---|---|
| us (user) | ユーザー空間のプロセス(nice値が0以下) | アプリの計算そのものが重い |
| sy (system) | カーネル空間。システムコールやページ管理 | システムコール過多、コンテキストスイッチ過多 |
| ni (nice) | nice値を正にして優先度を下げたプロセス | バッチ処理が回っている |
| id (idle) | 何もしていない時間 | CPUは余っている |
| wa (iowait) | アイドルのうち、I/O完了を待つタスクがあった時間 | ディスクやネットワークストレージが的 |
| hi (hardware irq) | ハードウェア割り込みの処理 | デバイスや割り込み設定の異常 |
| si (software irq) | ソフトウェア割り込みの処理 | ネットワークのパケット処理が重い |
| st (steal) | 仮想化でホストが他の仮想マシンへ回した時間 | ノイジーネイバーやCPUクレジット枯渇 |
サンプルはidが62.5で、8コアのうちおよそ5コア分が遊んでいる計算になる。一方でwaが10.8ある。waは「CPUがアイドルであり、かつそのCPUでI/O完了を待つタスクがある」時間を指す。idleの一部を切り出したものであって、CPUが忙しい時間ではない。
stは仮想化環境固有の項目で、自分の仮想CPUが動きたかったのに、ハイパーバイザが物理CPUを他の仮想マシンへ回していた時間だ。0.2程度なら誤差だが、数パーセントを超えて常態化するなら、バースト可能インスタンスでCPUクレジットが尽きたか、ホストが過密かを疑う。
メモリ2行 — used と buff/cache と avail Mem
Mem行はtotal・free・used・buff/cacheの4項目で、後ろ3つの合計がtotalに一致する。freeが412.8しか無いのを見て慌てるのは早い。buff/cacheの6541.4は、カーネルがファイル内容をキャッシュして再読み込みを避けるための領域で、アプリがメモリを要求すれば直ちに解放される。実質的に空きに近い。
そこで見るべきがavail Memだ。この値はMem行ではなくSwap行の末尾に間借りし、直前の区切りがカンマではなくピリオドなのがこの行の癖である。意味は「スワップを起こさずに新しいプロセスへ渡せるメモリの見積もり」で、解放可能なページキャッシュとスラブを勘定して算出する。6402.7あり、メモリの余裕はまだある。この4項目はfreeの出力を読むでさらに踏み込む。
Swap行のusedが128.0ある点も、「今使っている量」ではなく「これまでに追い出された結果の残量」であり、増えていないなら過去の名残にすぎない。危険なのはusedが増え続けている状態だ。
プロセス表 — PR/NI と VIRT・RES・SHR
PIDとUSERは説明を要さない。PRとNIは優先度の組だ。NIはnice値で、-20(最優先)から19(最劣後)まで取り、既定は0。PRはカーネルが使う内部優先度で、通常のプロセスではNIに20を足した値になる。サンプルのbackup.shはNIが10なのでPRは30だ。リアルタイムのプロセスではPRにrtと表示される。スケジューラの仕組みはOS側の話題になる。
続くVIRT・RES・SHRがメモリ3兄弟で、ここが最も誤読される。
| 列 | 何を数えているか | 実務での重み |
|---|---|---|
| VIRT | 確保した仮想アドレス空間の総量。触っていない予約分、共有ライブラリ、メモリマップしたファイル、スワップ済みのページまで含む | 小。大きくても問題とは限らない |
| RES | 実際に物理メモリ上に載っているページの量。スワップ済みは含まない | 大。まずここを見る |
| SHR | RESのうち他プロセスと共有され得る部分。共有ライブラリやファイルキャッシュ由来 | 中。実消費の差し引きに使う |
VIRTが約4.6GiBあるのにRESは約958MiBというjavaの行が典型例だ。JVMやGoのランタイムは起動時に広大なアドレス空間を予約するが、予約はアドレス番号の割り当てにすぎず、物理メモリは実際に触ったページにしか割り当たらない。監視すべきはRESの増加傾向である。
S 列 — プロセス状態の5文字
| S | 状態 | 意味 |
|---|---|---|
| R | Running / Runnable | CPUで実行中、または実行可能でCPUの順番待ち |
| S | Interruptible Sleep | イベント待ち。シグナルで起こせる。大半のプロセスはここ |
| D | Uninterruptible Sleep | 主にディスクI/O待ち。シグナルで割り込めない |
| Z | Zombie | 終了済みで親の回収待ち。CPUもメモリも消費しない |
| T | Stopped | SIGSTOPやCtrl+Zで停止、またはデバッガによるトレース停止 |
Dは特別扱いに値する。Dにいるプロセスはシグナルを受け付けないため、kill -9 を撃っても落ちない。NFSのハングやディスク障害でDのプロセスが積み上がると、CPUは暇なのにロードアベレージだけが跳ね上がる。サンプルの kworker/u16:2 がまさにDで、%Cpu(s)行のwaが10.8ある事実と符合する。同じI/O遅延を別角度から映したものだ。
%CPU・%MEM・TIME+ — 3つの物差し
%CPUは、前回の描画からの区間でそのプロセスがCPU時間をどれだけ占めたかを示す。既定はIrixモードで、1つのコアを使い切った状態を100%とする。8コアなら上限は800%であり、javaの187.3は「およそ1.9コア分を回している」と読む。
%MEMはRESを物理メモリの総量で割った比率だ。VIRTではなくRES基準である点に注意する。
TIME+は起動してから積み上げたCPU時間で、単位は分・秒・100分の1秒だ。経過時間ではない点が肝で、8スレッドを回すプロセスは実時間の8倍の速さで積む。javaの142分は、12日稼働のホストなら決して多くない。
COMMANDは既定でプログラム名だけを表示する。同名のプロセスが並んで区別が付かないときは、c で引数付きのコマンドラインへ切り替わる。
top起動直後の%CPUは、プロセスが生まれてからの累積CPU時間を稼働時間で割った値で、直近の負荷ではない。2画面目以降で初めて区間の増分になる。一瞬で判断せず、数回の更新を待つ。
つまずきやすい点
第一に、ロードアベレージをCPU使用率だと思うこと。ロードアベレージが数えるのは実行可能(R)なプロセスに加えて割り込み不可能な待ち(D)に入ったプロセスであり、後者はCPUを1サイクルも使っていない。ディスクやNFSが詰まっただけで、idが90を超えたままロードアベレージが50に達することがある。D状態を負荷に数えるのはLinux固有の仕様で、伝統的なUnixは実行可能なプロセスしか数えない。「ロードが高い=CPUが足りない」は、最も高価な誤診断だ。
第二に、ロードアベレージを絶対値で見ること。この数字にコア数の情報は入っていないため、単体では混雑度を意味しない。8コアなら8.0でおおよそ満杯、サンプルの3.42は半分以下の混み具合だ。同じ8.0でも1コアの環境なら意味がまるで違う。コア数は nproc で確認し、必ず割る。
第三に、waとstの読み違い。waはアイドル時間の一部であって、CPUが働いている時間ではない。waが高いときにCPUを増やしても改善せず、的はストレージだ。逆にstはゲスト側から一切制御できない。慢性的に数パーセントを超えるなら、インスタンスタイプの変更かホストの移動しか手が無い。
第四に、VIRTとRESの混同。VIRTは確保した仮想アドレス空間、RESは実際に載っている物理メモリだ。VIRTが数十GBでもRESが数百MiBなら健全である。RESにも罠がある。共有ライブラリのように共有されたページが各プロセスへ二重計上されるため、全プロセスのRESを足すと物理メモリを軽く超える。合計に意味は無い。
第五に、%Cpu(s)と%CPUの基準の違い。%Cpu(s)行は全コアの平均を100として配分するので合計は100に収まる。一方プロセス表の%CPUは1コアを100とするので、合計はコア数×100まで伸びる。同じ画面のパーセントでも分母が違う。%Cpu(s)行で 1 を押せばコアごとに分解でき、1コアだけ張り付いているのか全体が均等に忙しいのかを見分けられる。
topはプロセス単位のI/O量を見せない。waが高いと分かっても、どのプロセスが待たせているかまでは辿れない。そこからは iotop や pidstat -d へ移る。topは容疑者を絞る道具であって、自白を取る道具ではない。
まとめ
topのサマリ5行は、それぞれ別の問いに答えている。ヘッダ行は混んでいるか、Tasks行は異常な状態のプロセスがいるか、%Cpu(s)行はCPU時間が何に使われたか、メモリ2行は本当に足りないか。そしてプロセス表が、誰がという最後の問いを引き受ける。
読む順番も決まっている。まずロードアベレージの3つの数字で傾きを掴み、コア数で割る。次に%Cpu(s)でus・sy・wa・stのどれが太いかを見て、CPU律速かI/O律速かホスト起因かを切り分ける。方向が決まってから、初めてプロセス表へ降りて犯人を探す。上から下へ、全体から個へ。この順番さえ守れば、topは3秒で仮説をくれる。他のコマンドの読み方はコマンド出力の読み方に揃えている。
コマンド出力の読み方の記事ガイド
top の出力を1行ずつ解剖を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
コマンド出力
比較で見る軸
難易度: advanced / カテゴリ: コマンド出力の読み方 / タグ数: 4
導入後に効く点
%Cpu(s)のusはユーザー空間、syはカーネル、waはI/O待ち、stは仮想化ホストに奪われた時間。wa高ならストレージ、st高なら同居VMやクレジット枯渇を疑う。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- コマンド出力の読み方
- タグ数
- 4
判断チェックリスト
- 自社の用途が「コマンド出力 / Linux」に近いか確認する。
- 強みである「ロードアベレージは1・5・15分の指数移動平均で、実行可能な処理と割り込み不可のI/O待ちを数える。コア数で割ると混雑度になり、8コアなら8.0がおおよそ満杯だ。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。