ロードアベレージ4.0は高いのか:Linuxの負荷平均が数えるものと1・5・15分の意味
LinuxのロードアベレージはCPU使用率ではなく、実行中・実行待ち(R)と割り込み不可の待機(D)のタスク数の指数平均です。カーネルソースとmanページから計算方法、CPU数との読み方、PSIとの違いをまとめました。
uptimeやtopの1行目にあるload average: 3.95, 2.10, 1.02はパーセントではありません。Linuxではこの3つの数は、CPUを使っているかCPUを待っているタスクと、割り込み不可の待機(D状態)にあるタスクを合わせた数を、1分・5分・15分の単位で平均した値です。同じ4.0でも、CPUがいくつあるか、タスクが何を待っているかで意味が変わります。
規格で決まった値ではない
POSIXにはロードアベレージの定義がありません。値を読むC関数getloadavg()も、Linuxのmanページでは規格が「BSD」とされ、4.3BSD-Renoで登場しました。LinuxとFreeBSDのgetloadavg(3)はどちらも「run queueにあるプロセス数の平均」と説明しますが、何をrun queueとして数えるかは各カーネルが決めます。この記事はLinuxカーネルが対象で、ほかのOSの数え方は確認していません。
Linuxが数えるもの:RとD
proc_loadavg(5)は、/proc/loadavgの最初の3フィールドを、run queueにある(state R)タスクとディスクI/Oを待つ(state D)タスクの数の平均だと説明しています。カーネルソースkernel/sched/loadavg.cのコメントはもっと直接的で、全体のロードアベレージはnr_running + nr_uninterruptibleの指数減衰平均だと書かれています。
D状態の代表的な原因はディスク待ちですが、カーネルが見るのは待つ理由ではなく状態フラグです。タスクがTASK_UNINTERRUPTIBLEで眠ると負荷に入り、TASK_NOLOADが付いた状態(TASK_IDLE)や凍結されたタスクは除かれます(kernel/sched/core.cのblock_task())。ps(1)がDを「uninterruptible sleep (usually I/O)」と「通常は」付きで説明するのはこのためです。
そのため、CPUは空いているのにロードアベレージだけが高いことがあります。応答の遅いストレージを待つタスクがD状態でたまると、CPU使用率が低くても数値は上がります。値が高いときは、psでスレッドまで表示して(例:ps -eLo pid,lwp,stat,wchan,comm)、RとDのタスクがどのプロセスに偏っているか、Dのタスクがカーネルのどこで待っているかを見ると手がかりになります。ただし、psで数えたある瞬間の数は、カーネルが標本を取って平均するロードアベレージをそのまま再現するものではありません。
1分の値は直近1分の単純平均ではない
カーネルはLOAD_FREQ(5秒+1ティック)ごとにその瞬間のタスク数を標本として取り、前の値と混ぜます。
新しい値 = 前の値 × e + 現在のタスク数 × (1 − e)
係数eはinclude/linux/sched/loadavg.hに、2048を1.0とする固定小数点で入っています。1分用のEXP_1は1884、5分用は2014、15分用は2037で、それぞれe^(−5秒/1分)、e^(−5秒/5分)、e^(−5秒/15分)に2048を掛けた値です。
この係数で計算すると、0だったシステムで実行可能なタスクが4つ続けて存在するようになった場合、1分後(標本12回)の1分値は 4 × (1 − (1884/2048)^12) ≈ 2.5 です。新しい状態の約63%しか反映されず、1分より古い標本も小さな重みで残ります。5分値と15分値はさらにゆっくり動くので、3つを並べると負荷が上がっている途中か下がっている途中かの見当がつきます。カーネルは整数で計算するため、小数部はこの計算と少しずれることがあります。
CPU数と合わせて読む
uptime(1)は、ロードアベレージがCPU数で正規化されていないと明記しています。manページの例では、同じ1でもCPU1個のシステムならずっと忙しかったという意味です。CPUが4個のシステムでは、CPU容量全体の約75%、つまり平均でCPU 3個分がアイドルだったことを意味します。したがって、ほとんどがRタスクの4.0は、論理CPU16個のサーバーなら余裕があり、2個のサーバーなら実行待ちの列ができていると読めます。「CPU数を超えたら危険」といった基準は経験則で、D状態を含むLinuxの値にはそのまま当てはまりません。
/proc/loadavgにはあと2つ値があります。次は例の値です。
0.52 0.48 0.40 2/613 12345
4番目のフィールドの/の前は今実行可能なスケジューリング単位(プロセス・スレッド)の数、後ろは存在する総数で、5番目は最後に作られたPIDです。カーネルソースでは/の前の値をnr_running()で埋めるので、平均ではなく瞬間値で、D状態は含みません。
何を待っているかはPSIで見る
カーネルがCONFIG_PSI付きでビルドされていれば、/proc/pressure/cpu、memory、ioで、資源ごとにタスクが止まっていた時間の割合を10秒・60秒・300秒の平均で見られます。someは一部のタスクでもその資源のせいで止まっていた時間の割合、fullはアイドルでないタスクすべてが同時に止まっていた時間の割合です。システム全体のcpuのfullは定義されておらず、5.13以降は互換性のため0が出力されます。ロードアベレージがタスク数を1つの数にまとめるのに対し、PSIは待った時間を資源ごとに分けて示します。
開いたままのファイルのせいでディスク容量が戻らない問題はunlinkと開いたままのファイルで扱いました。
参考資料
- Linux man-pages 6.19:proc_loadavg(5)、getloadavg(3)、proc_pid_stat(5)
- procps-ng:uptime(1)、ps(1)
- Linuxカーネルソース(v7.2タグで確認):kernel/sched/loadavg.c、include/linux/sched/loadavg.h、kernel/sched/core.c、include/linux/sched.h、fs/proc/loadavg.c
- Linuxカーネル文書:PSI - Pressure Stall Information
- FreeBSD 15.1:getloadavg(3)