プロセス会計とリソース使用量の計測機構
topやmemory.statの数字が「どう作られ、どこまで正確か」を原理から押さえれば、メトリクスの誤読を避けられます。rusageの集計、CPU時間のuser/sys/steal按分、cgroup統計とPSIまで一気に整理します。
- 1.getrusage と /proc は同じ実体を別の窓から見せる。前者は wait で子の使用量も合算でき、後者は ksamp と tick 加算でカーネルが常時更新する積算値を読み出す。
- 2.CPU時間は割り込みやティック境界でその瞬間に走っていたコンテキストへ丸ごと計上される。user/sys に加え、仮想化では steal(VCPUが物理CPUを奪われ待たされた時間)が現れる。
- 3.cgroup統計は絶対量、PSIは「待たされた時間の割合」で逼迫を測る。tickベースの計上は粒度が粗く、サンプリング誤差とオーバーヘッドのトレードオフが常にある。
計測の二つの窓口:rusage と /proc
カーネルはプロセスごとに資源使用量を task_struct の中に積算し続けています。ユーザー空間がそれを覗く窓口は大きく二つあります。一つは getrusage(2) システムコールで、もう一つは /proc/<pid>/ 以下の擬似ファイルです。両者は別実装のように見えて、根は同じカウンタを別の角度から読み出しているにすぎません。
getrusage は who 引数で対象を切り替えます。RUSAGE_SELF は呼び出しプロセスの全スレッドの合算、RUSAGE_THREAD は呼び出しスレッドだけ、RUSAGE_CHILDREN はすでに wait で回収済みの子孫の合算です。最後の点が重要で、まだ生きている子の使用量は含まれません。子の rusage は終了時に親へ畳み込まれ、親が wait4 でそれを回収した瞬間に RUSAGE_CHILDREN へ加算されます——これはゾンビプロセスが回収(リープ)されるのと同じ契機です。回収されないまま親が無視すれば、子の会計情報もろとも失われます。
返ってくる struct rusage の中身は、CPU時間(ru_utime/ru_stime)、最大常駐集合 ru_maxrss、ページフォルト数(マイナー ru_minflt/メジャー ru_majflt)、自発・非自発のコンテキストスイッチ回数などです。一方 /proc/<pid>/stat はこれらをスペース区切りの一行に並べ、/proc/<pid>/status は人間可読な key: value 形式で、/proc/<pid>/io はI/Oのバイト数(rchar/wchar のVFS層を通過した量と、read_bytes/write_bytes の実ブロック層の量)を公開します。
ru_maxrss は Linux ではキロバイトですが、瞬間値ではなくプロセス生涯のピーク常駐量です。今まさに使っているメモリではないため、解放後に下がることはありません。さらに RUSAGE_CHILDREN の ru_maxrss は子の合計ではなく最大の子1つのピークを表すなど、フィールドごとに合算規則が異なります。man ページで「Linux で未維持(常に0)」とされるフィールドも複数あり、移植時の誤読の温床です。
CPU時間はどう「按分」されるのか
CPU使用量の会計には、根本的な近似が潜んでいます。古典的な実装では、カーネルは周期的なタイマ割り込み(ティック、典型的には100~1000Hz)のたびに「いま割り込まれたのは誰か」を見て、そのティック1つ分をまるごとそのコンテキストに計上します。ユーザーモードで走っていれば user 時間、カーネルモードなら system 時間へ加算されます。詳しくは高分解能タイマとカーネルの時間管理が扱う tick 機構そのものです。
この「丸ごと計上」はサンプリングである以上、誤差を含みます。1ティックより短く走って次のティック前に眠るタスクは時間を計上されず、逆にティック境界をまたいだ瞬間に走っていたタスクは実際以上に課金され得ます。多数の短命タスクではこの偏りが効いてきます。
| 区分 | 計上される時間 | 意味 |
|---|---|---|
| user | ユーザーモードでの実行 | アプリのコード自身がCPUを使った時間 |
| sys(system) | カーネルモードでの実行 | システムコールや割り込み処理など、そのタスクの代理でカーネルが使った時間 |
| steal | VCPUが実行可能なのに物理CPUを与えられなかった時間 | 仮想化環境でハイパーバイザが他VMへCPUを回し、待たされた時間 |
| iowait | I/O完了待ちでアイドルだった時間 | 厳密にはCPUの状態であってプロセス個別の課金ではない(誤読注意) |
steal 時間は仮想化に固有です。ゲストの VCPU は走る用意ができているのに、ハイパーバイザが物理CPUを別のゲストへ割り当てているため進めない——その待ち時間がスチールです。ゲスト内の top で user+sys が低いのに応答が遅いとき、st 列が高ければ原因はゲストではなくホスト側の過密です。これはゲストOSからは直接制御できず、ホストの会計と突き合わせて初めて切り分けられます。
ティック近似の誤差を嫌う場面のために、Linux はコンテキスト境界で TSC 等のクロックを読む精密会計(VIRT_CPU_ACCOUNTING)を持ちます。ユーザー/カーネルの遷移やコンテキストスイッチのたびに経過サイクルを実測して按分するため、tickless 動作(NO_HZ)とも相性が良く誤差が小さい一方、遷移ごとにクロック読み出しのコストが乗ります。粒度を取るか軽さを取るかの典型的なトレードオフです。
cgroup統計とPSI:絶対量と「待たされ具合」
個々のプロセスではなく、コンテナやサービス単位の集計は cgroup が担います。cgroup v2 では cpu.stat が配下タスクの usage_usec/user_usec/system_usec をマイクロ秒で公開し、スロットルがかかった回数(nr_throttled)や合計スロットル時間も併記します。CPU上限に達してタスクが走れずに待たされた量が、ここに数値として現れます。メモリ側は memory.stat が anon/file/slab などの内訳を出し、これはメモリcgroupによるリソース制御で扱う charge の集計結果そのものです。
ただし絶対量だけでは「どれだけ困っているか」は分かりません。CPU使用率が80%でも余裕のこともあれば、リクレイムでCPUが空転して使用率が見かけ上高いだけのこともあります。そこで cgroup v2 は PSI(Pressure Stall Information) を提供します。PSIが測るのは使用量ではなく、リソース待ちで実行が止まっていた時間の割合です。
cpu.pressure/memory.pressure/io.pressure にはそれぞれ some と full の行が出ます。some は「少なくとも1タスクがそのリソース待ちで stall した時間の割合」、full は「実行可能な全タスクが同時に stall し、CPUが前進していなかった割合」です。full が高い区間は、その cgroup が実質的に何も進めていなかったことを意味します。
| 指標の種類 | 代表ファイル | 性質と読み方 |
|---|---|---|
| 絶対量(積算) | cpu.stat / memory.stat | 使用量そのもの。差分を取れば区間レートになるが、機種・負荷で意味が変わる |
| PSI some | *.pressure の some 行 | 誰か1人でも待った割合。0でなければ圧力の早期サイン |
| PSI full | *.pressure の full 行 | 全員が同時に止まった割合。スループット低下に直結する実害の指標 |
PSIの本質は、CPU・メモリ・I/Oを同じ「stall時間の割合」という単位で横並びにできる点にあります。絶対量は機種依存ですが、待たされた割合は機種非依存で比較でき、しきい値トリガを poll() で待てば逼迫の先回り検知もできます。
オーバーヘッドと正確性のトレードオフ
会計はタダではありません。tickベースの計上は割り込みのついでに加算するだけなので安価ですが、前述の通り粒度が粗くサンプリング誤差を持ちます。精密会計は正確ですが遷移ごとにクロックを読みます。/proc の読み出しはさらに別のコストで、ファイルを open/read するたびにカーネルがその場でテキストを生成(フォーマット)するため、数千プロセスを高頻度でスキャンする監視エージェントは、計測対象に無視できない負荷を自ら加えます。観測が対象を乱す——いわばプローブ効果です。
/proc/<pid>/stat の各フィールドはアトミックなスナップショットではありません。読み出し中に値が更新され得るため、一行の中で時点の異なる値が混在することがあります。CPU使用率を出すには2時点の utime+stime の差分を実時間で割りますが、その2回の読み出しの間隔にも生成コストとジッタが乗ります。短い区間で割ると tick 量子化の誤差が拡大するため、レート算出は十分長い窓で行うのが鉄則です。
伝統的な BSD プロセスアカウンティング(acct(2) による pacct 書き出し)は、プロセス終了時に1レコードを残す方式で、生存中の継続的なサンプリングとは別系統です。終了したプロセスを取りこぼさず会計できる利点がある一方、長時間走り続けるプロセスの「いまの」使用量は得られません。現代の監視は、生存中は /proc・cgroup・PSIで継続観測し、終了は wait の rusage や pacct で締める、という二系統の併用が実態です。
まとめ
getrusageと/proc/cgroup は同じ積算カウンタを別の窓から見る関係。子の使用量はwaitで回収した瞬間にRUSAGE_CHILDRENへ畳み込まれ、ru_maxrssは生涯ピークでフィールドごとに単位・合算規則が異なる。- CPU時間はティック境界での「丸ごと計上」が基本で user/sys に分かれ、仮想化では VCPU が物理CPUを奪われた steal が現れる。精密会計はコンテキスト境界で実測し誤差を抑える代わりにコストが乗る。
- cgroup統計は絶対量、PSI は待たされた時間の割合(some/full)で逼迫を測り、両者は補完関係にある。
- tick量子化のサンプリング誤差、
/proc生成コスト、スナップショット非アトミック性——計測は常にオーバーヘッドと正確性のトレードオフを抱える。レートは十分長い窓で取るのが安全。
OS Article
プロセス会計とリソース使用量の計測機構を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
プロセス会計
比較で見る軸
難易度: advanced / カテゴリ: OS / タグ数: 6
導入後に効く点
CPU時間は割り込みやティック境界でその瞬間に走っていたコンテキストへ丸ごと計上される。user/sys に加え、仮想化では steal(VCPUが物理CPUを奪われ待たされた時間)が現れる。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- OS
- タグ数
- 6
判断チェックリスト
- 自社の用途が「プロセス会計 / getrusage」に近いか確認する。
- 強みである「getrusage と /proc は同じ実体を別の窓から見せる。前者は wait で子の使用量も合算でき、後者は ksamp と tick 加算でカーネルが常時更新する積算値を読み出す。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。