Skip to content
Published on

htopとtopを完全に読む — すべての数字の意味

シェア
Authors

はじめに — 数字の洪水

Linuxサーバーで htoptop を初めて開くと、数字と色の洪水に圧倒されます。多くの人はCPUとメモリのパーセントだけを何となく見て残りを無視しますが、その数字一つひとつには、システムの状態を診断するのに必要な情報が詰まっています。最近Hacker Newsの上位に再び上がったペテリス・ニキフォロフス(Pēteris Ņikiforovs)の名解説「Explanation of everything you can see in htop」がこの話題を再び照らしたのを機に、この記事ではそれらのフィールドを一つずつ分解します。

プロセスの列を読む

プロセス一覧の各列が何を意味するかから見ていきましょう。

  • PID: プロセスID。プロセスが起動するときに割り当てられる固有の番号です。
  • USER: プロセスの所有者。そのプロセスを実行したユーザーです。
  • PRINI: 優先度(priority)とナイス(nice)値です。関係は PR = 20 + NI で表されます。ナイス値は -20(最も高い優先度)から 19(最も低い優先度)までで、ナイスが低いほどCPUを多く割り当てられます。おおまかな目安として、ナイス一段階ごとに約10%のCPU時間が移動すると考えます。
  • VIRT / RES / SHR: メモリ関連の三つの値で、下で別に扱います。
  • S: プロセスの状態(state)で、これも下で扱います。
  • %CPU: CPU使用率です。
  • %MEM: RES / 全RAM で計算されるメモリ使用率です。
  • TIME+: プロセスがこれまで累積で使ったCPU時間です。

VIRT、RES、SHR — メモリ三値の罠

メモリの列は最もよく誤解される部分です。三つの値の意味はまったく異なります。

  • VIRT(仮想メモリ): プロセスが確保した仮想アドレス空間の全体です。実際には使わない領域、共有ライブラリ、メモリマップドファイル、スワップに追い出されたページまですべて含みます。そのためVIRTはたいてい膨らんでおり、実際のメモリ使用量の指標としてはほとんど役に立ちません。
  • RES(常駐メモリ): スワップされず、実際に物理RAMに載っている、プロセスが実際に使っているメモリです。「このプロセスが今RAMをどれだけ食っているか」を知りたいなら、この値を見ます。
  • SHR(共有メモリ): 他のプロセスと共有され得るメモリです。代表的には共有ライブラリがこれにあたります。

ここで重要な実務上の教訓が一つ出ます。メモリを多く食うプロセスを探すときは、VIRTではなく RESで並べ替える べきだということです。VIRTで並べ替えると、実際にはメモリをあまり使っていないのにアドレス空間だけ大きく取ったプロセスが上位に来て、判断を誤りやすくなります。

プロセス状態の文字

S 列には一文字のプロセス状態が表示されます。各文字の意味は次のとおりです。

  • R — 実行中または実行待ち(runnable)。CPUで動いているか、順番を待っている状態です。
  • S — 割り込み可能なスリープ(interruptible sleep)。あるイベントを待って眠っていますが、シグナルで起こせます。平常時はほとんどのプロセスがこの状態です。
  • D — 割り込み不可能なスリープ(uninterruptible sleep)。通常はディスク入出力を待つ状態で、シグナルでも起こせません。
  • Z — ゾンビ(zombie)。終了したが親がまだ回収していない状態です。
  • T — ジョブ制御シグナルで停止した状態(例:Ctrl+Z)。
  • t — デバッガ/トレーサーによって停止した状態です。

ロードアベレージ — CPU使用率とは違う

最も広く誤解される指標が ロードアベレージ(load average) です。よく「CPU使用率」と同じだと思われますが、そうではありません。

第一に、ロードアベレージは単純な平均ではなく 指数減衰移動平均 です。1分、5分、15分の三つの値が表示され、最近の値により大きな重みを置きます。1分値はおおよそ、直近1分の負荷を63%、それ以前を37%ほど混ぜた値です。

第二に、そしてより重要な点 — ロードは実行中の(R)プロセスだけでなく、割り込み不可能なスリープ(D)状態のプロセスも一緒に数えます。 このため、ロードが高いからといって必ずしもCPUが忙しいわけではありません。ディスクやネットワークの入出力を待つプロセスが多くても、ロードは跳ね上がります。

第三に、ロードはコア数を基準に解釈すべきです。N個のコアを持つマシンでロードがNなら、おおよそ100%の利用状態です。コアが4個でロードが4なら飽和状態、コアが8個でロードが4なら半分しか使われていないことになります。

これら三つを総合すると強力な診断ルールが出ます。ロードは高いのに %CPU が低ければ、十中八九は入出力待ち(I/O wait) です。このときは D 状態のプロセスを探すのが問題解決の出発点です。

メーターの色

htop上部のバーメーターも色で情報を伝えます。

  • CPUメーター: 青は低優先度(ナイス値が正の)スレッド、緑は通常優先度のスレッド、赤はカーネルスレッドを表します。(htopはこのほかに仮想化や入出力待ちなどをさらに細かく表示することもあります。)
  • メモリメーター: 緑は実際に使われるメモリ(used)、青はバッファ(buffers)、オレンジはキャッシュ(cache)です。

ここでバッファとキャッシュを「使われているメモリ」と勘違いしないことが重要です。バッファとキャッシュはカーネルが性能のために一時的に借りている領域で、アプリケーションがメモリを必要とすればすぐに返されます。ですから実際に注目すべき値は「used」ではなく 利用可能メモリ(available) です。Linuxで「メモリがほぼ一杯だ」という画面がたいてい問題ではない理由がこれです。キャッシュで埋まったメモリは、必要なときに空けられる、事実上の余裕メモリです。

ゾンビプロセスの正体

ゾンビ(Z)プロセスはよく誤解されます。いくつかの事実を整理します。

ゾンビはすでに死んだプロセスです。実行を終えましたが、親プロセスがその終了ステータスをまだ回収(reap)していないため、プロセステーブルに項目だけが残っている状態です。そのためゾンビは メモリをまったく消費しません。 ただプロセステーブルのスロットを一つ占めるだけです。

また、ゾンビは 殺せません。 プロセスを終了させるにはシグナルを送りますが、シグナルは生きているプロセスしか受け取れません。すでに死んだゾンビに kill -9 を送っても無駄です。ゾンビが消える唯一の道は、親がそれを回収することです。もし親が先に死ぬと、ゾンビはinit(PID 1)に引き取られ、initが代わりに回収します。

つまりゾンビが数個見えても慌てる必要はありません。ゾンビが積み上がり続けるなら、それはゾンビ自体が問題というより、子を正しく回収しない親プロセスのバグを示す信号です。

コピーオンライトとRESの錯覚

一つ微妙な現象を付け加えます。プロセスが fork で子を作ると、Linuxは コピーオンライト(copy-on-write) を使います。親と子が同じ物理メモリページを共有し、どちらかが書き込みをするときにだけそのページを複製します。

このため、htopで親と子がそれぞれ全RESをまるごと表示することがあります。見かけ上はメモリを二倍使っているようですが、実際の物理メモリは共有されており、合計はそれほど大きくありません。プロセスを多くforkするプログラムのメモリを見るときは、この錯覚を念頭に置く必要があります。

実戦の診断レシピ

ここまでの内容を実際のトラブルシューティングに適用すると、次のようになります。

  • 「ロードは高いのにCPUは暇」 → 入出力待ちです。D 状態のプロセスを探し、ディスクやネットワークのボトルネックを疑います。
  • メモリを食う犯人を探す → VIRTではなくRESで並べ替えます。
  • 暴走するスレッドを探す → htopでスレッド表示をオンにし、%CPU が持続的に高い項目を見ます。
  • 「メモリが一杯だ」 → usedではなくavailableを見ます。キャッシュで埋まったものは問題ではありません。

これらのツールをブラウザで直接触ってみたいなら、このサイトの LinuxターミナルWebターミナル を、カーネルパラメータがこうした動作にどう関わるかを見たいなら カーネルパラメータ探索 を併せて開いてみるとよいでしょう。

おわりに

htopとtopの数字は最初は暗号のように見えますが、各フィールドの意味を知れば、システムの状態を読む強力な診断ツールになります。特にVIRTとRESの違い、ロードが入出力待ちを含むという事実、キャッシュが事実上の余裕メモリだという点 — この三つをきちんと理解するだけで、サーバー問題の診断の半分は解決します。

参考資料