Linux の PSI の読み方: /proc/pressure の some・full・avg・total

/proc/pressure の some と full、avg10・avg60・avg300、total がそれぞれ何を測っているのか、iowait との違い、しきい値トリガーの制約をカーネルドキュメントに沿って整理します。

LinuxPSIパフォーマンス監視cgroup

CPU 使用率やメモリ使用量からわかるのは、リソースがどれだけ使われているかです。処理がそのリソースを待って実際にどれだけ止まっていたかは、これらの数字だけでは判断しにくいものです。Linux の PSI(Pressure Stall Information)は、この「止まっていた時間」を CPU、メモリ、I/O ごとに出力するインターフェースです。この記事では カーネルの PSI ドキュメント をもとに、各数値の意味と読み方の限界をまとめます。

ファイルの形式

システム全体の圧力情報は /proc/pressure/ 以下の cpu、memory、io の 3 ファイルにあります。形式は次のとおりです。

some avg10=0.00 avg60=0.00 avg300=0.00 total=0
full avg10=0.00 avg60=0.00 avg300=0.00 total=0
項目 意味
some 行 少なくとも一部のタスクがそのリソースのために停止していた時間の割合
full 行 アイドルでないタスクのすべてが同時にそのリソースのために停止していた時間の割合
avg10、avg60、avg300 それぞれ 10 秒、60 秒、300 秒の区間での最近の傾向。単位は %
total 累積の絶対停止時間。単位はマイクロ秒(µs)

full は some の部分集合です。カーネルドキュメントは full の状態を実際の CPU サイクルが無駄になっている状態と説明し、この状態に長くとどまるワークロードはスラッシングしているとみなす、と書いています。逆に some だけが上がっているなら、一部のタスクは待っていても CPU はまだ有用な処理を続けている可能性があります。2 つの行を分けて集計しているのはこの区別のためです。

/proc/pressure/cpu の full は 0 になる

ドキュメントによると、CPU の full はシステムレベルでは定義されていません。ただし 5.13 からこの行が出力されるようになり、後方互換のため値は 0 に固定されています。そのため /proc/pressure/cpu の full が 0 であることから CPU に余裕があるとは判断できません。CPU については some 行を見ます。この 0 固定のルールは、ドキュメントではシステムレベルについて書かれたものです。

total で任意の区間の割合を求める

avg の値は 10 秒、60 秒、300 秒の窓に固定されています。ドキュメントは total を別に出力する理由を 2 つ挙げています。時間平均にはほとんど表れない短いレイテンシのスパイクを検出するため、そして任意の期間で自分で平均を取るためです。

total はマイクロ秒単位の累積値なので、2 回読んだ差をその間の経過時間で割れば、その区間の停止割合になります。

cat /proc/pressure/io
sleep 5
cat /proc/pressure/io

たとえば(説明用の仮の値)、some 行の total が 1,200,000 から 1,950,000 に増え、その間が 5 秒だったとします。

(1,950,000 − 1,200,000) µs ÷ 5,000,000 µs = 0.15 → 15%

この 5 秒間のうち約 15% の時間、少なくとも一部のタスクが I/O のために止まっていた、という意味になります。

iowait、procs_blocked、ディスク統計との違い

/proc/stat の CPU 行にも iowait 列があります。ただし カーネルの /proc ドキュメント は、この値についていくつかの問題を自ら挙げています。

  • I/O の完了を待つのは CPU ではなくタスクです。タスクが I/O を待って CPU がアイドルになると、その CPU には別のタスクがスケジュールされることがあります。
  • マルチコアでは、I/O を待っているタスクはどの CPU でも実行されていないため、CPU ごとの iowait は計算が難しくなります。
  • /proc/stat の iowait の値は、特定の条件で減少することがあります。

ドキュメントの結論は「/proc/stat から読んだ iowait は信頼できない」です。同じファイルの procs_blocked は、現在 I/O の完了待ちでブロックされているプロセスの数で、読んだ瞬間の値です。どれだけの時間が積み上がったかはわかりません。

ディスクごとの統計のフィールド 10(I/O に費やしたミリ秒。/proc/diskstats ではメジャー・マイナー番号とデバイス名の後ろから数えて 10 番目の値)はデバイス側の視点です。iostats ドキュメント によると、この値は処理中の I/O がある間は増え続け、5.0 以降はリクエストが開始または完了した jiffy を数えます。リクエストが 2 jiffy より長くかかると、同時リクエストがある場合に一部の I/O 時間が計上されないことがあります。デバイスが忙しかったかどうかと、処理が I/O のために止まったかどうかは別の問いで、PSI の io ファイルが扱うのは後者です。

ロードアベレージは 別の記事 で扱っています。メモリ使用量の数字の読み方は free の available の記事 を参照してください。使用量が多いことと、メモリ不足で処理が止まったことは別の事実で、後者を見るファイルが /proc/pressure/memory です。

しきい値トリガー: poll() で通知を受ける

ファイルを定期的に読む代わりに、停止時間がしきい値を超えたときに起こしてもらうトリガーを登録できます。該当する /proc/pressure/ のファイルを開いて次の形式の文字列を書き込み、そのファイルディスクリプタを select()、poll()、epoll() で待ちます。

<some|full> <停止時間 µs> <時間窓 µs>

ドキュメントの例は次のとおりです。

  • /proc/pressure/memory に some 150000 1000000: 1 秒の窓の中で部分的なメモリ停止が 150ms を超えたら通知
  • /proc/pressure/io に full 50000 1000000: 1 秒の窓の中で I/O の完全停止が 50ms を超えたら通知

ドキュメントに書かれた制約は次のとおりです。

  • 時間窓は 500ms から 10s まで受け付けられます。停止の増加は窓ごとに 10 回確認されるため、更新間隔は最小 50ms、最大 1s です。
  • 特権のないユーザーもトリガーを作成できますが、時間窓は 2 秒の倍数でなければなりません。
  • トリガー 1 つにつきファイルディスクリプタが 1 つ必要です。同じファイルでもトリガーごとに別々に open() する必要があり、すでにトリガーがあるディスクリプタに書き込むと EBUSY で失敗します。
  • モニターは対象の指標が停止状態に入ると有効になり、一度有効になると少なくとも 1 窓分は維持されます。通知は窓ごとに 1 回に制限されます。
  • トリガーは、登録に使ったファイルディスクリプタを閉じると解除されます。

最後のルールにより、シェルで echo "some 150000 1000000" > /proc/pressure/memory のように書くと、リダイレクトが終わった時点でディスクリプタが閉じられ、監視するトリガーは残りません。ディスクリプタを開いたまま待つプログラムが必要です。ドキュメントの C の例を短くすると、要点は次のとおりです。

const char trig[] = "some 150000 1000000";
struct pollfd fds;

fds.fd = open("/proc/pressure/memory", O_RDWR | O_NONBLOCK);
fds.events = POLLPRI;
write(fds.fd, trig, strlen(trig) + 1);

while (1) {
    poll(&fds, 1, -1);
    if (fds.revents & POLLERR) break;      /* イベントソースが消えた */
    if (fds.revents & POLLPRI) printf("event triggered!\n");
}

元の例には open()、write()、poll() のエラー処理が含まれているので、実際に使うときは 原文 を基準にしてください。

cgroup v2 での PSI

CONFIG_CGROUPS=y のカーネルで cgroup2 ファイルシステムがマウントされていれば、cgroup ごとにも PSI が集計されます。各 cgroup ディレクトリには cpu.pressure、memory.pressure、io.pressure があり、形式は /proc/pressure/ と同じです。トリガーもシステム全体と同じ方法で登録できます。

cgroup v2 ドキュメント にはさらに 2 つのファイルがあります。

  • irq.pressure: IRQ/SOFTIRQ の圧力情報を示すファイルとして挙げられています。ただし今回確認した版の PSI ドキュメントには、IRQ についての個別の説明はありません。
  • cgroup.pressure: 既定値は 1 で、0 を書き込むとその cgroup の PSI 集計が無効になります。この設定は階層的ではなく、子孫の cgroup には影響しません。PSI は cgroup ごとに停止を集計し階層の各レベルで合算するため、階層が深いと一部のワークロードで無視できないオーバーヘッドが生じることがあり、非リーフの cgroup で集計を止められるように用意された設定です。

読み方の限界

  • PSI はリソースごとに停止時間を合算した値です。どのプロセスが原因かは、これらのファイルだけではわかりません。cgroup ごとのファイルを比べれば、どのグループで停止が起きたかまでは絞り込めます。
  • カーネルドキュメントは、何 % から問題とみなすかという基準値を示していません。この記事も基準値は示しません。
  • avg の値は最近の傾向なので、短いスパイクは埋もれることがあります。ドキュメントが total を別に出力している理由です。
  • この記事は 2026-10-11 に確認した docs.kernel.org のドキュメント(7.3.0-rc6 ビルド)に基づいています。この記事で引用したドキュメントがバージョンを明記しているのは、「CPU の full は 5.13 から出力」と「diskstats のフィールド 10 は 5.0 以降、リクエストが開始または完了した jiffy を数える」の 2 点です。古いカーネルでは出力形式や対応ファイルが異なる可能性があります。

ブログへ