KVM で VM を動かしている際に、ホストが返す /proc/stat
数値の扱いにはちょっとした注意が必要
/proc/stat の man
/proc/stat について説明した man 5 proc には下記の通りの説明がある
user (1) ユーザーモードで消費した時間。
guest (Linux 2.6.24 以降) (9) Linux カーネルの制御下のゲストオペレーティングシステムの仮想 CPU の 実行に消費された時間。
実はこの user には guest 分が重複して加算されている。そんでもって、この仕様は man には記載されていない。(英語の man でも同様)
ソースで説明
KVM で VM を動かしている際に消費した CPU時間を計測しているのは account_guest_time
である。 (この記事を書いている時点で) 最新の 4.13-rc4 では下記の通りのソースだ
/* * Account guest cpu time to a process. * @p: the process that the cpu time gets accounted to * @cputime: the cpu time spent in virtual machine since the last update */ void account_guest_time(struct task_struct *p, u64 cputime) { u64 *cpustat = kcpustat_this_cpu->cpustat; /* Add guest time to process. */ p->utime += cputime; account_group_user_time(p, cputime); p->gtime += cputime; /* Add guest time to cpustat. */ if (task_nice(p) > 0) { cpustat[CPUTIME_NICE] += cputime; cpustat[CPUTIME_GUEST_NICE] += cputime; } else { cpustat[CPUTIME_USER] += cputime; ⭐ cpustat[CPUTIME_GUEST] += cputime; ⭐ } }
cpustat[CPUTIME_USER]
が user 時間cpustat[CPUTIME_GUEST]
が guest 時間
両者に cputime
を加算しているのがポイント
この計測方法は v2.6.24-rc1 から取り入れられている。その時の実装を貼る
/* * Account guest cpu time to a process. * @p: the process that the cpu time gets accounted to * @cputime: the cpu time spent in virtual machine since the last update */ void account_guest_time(struct task_struct *p, cputime_t cputime) { cputime64_t tmp; struct cpu_usage_stat *cpustat = &kstat_this_cpu.cpustat; tmp = cputime_to_cputime64(cputime); p->utime = cputime_add(p->utime, cputime); p->gtime = cputime_add(p->gtime, cputime); cpustat->user = cputime64_add(cpustat->user, tmp); ⭐ cpustat->guest = cputime64_add(cpustat->guest, tmp); ⭐ }
現在のと比較してリファクタリングされているのがわかるが、意味するところは変わらない
ユーザランドツールの実装をどうすべきか
mpstat を参考にする。先に述べた点を考慮して mpstat は user から guest を減算して補正している
https://github.com/sysstat/sysstat/blob/master/mpstat.c#L529-L568 (d7321c1)
/* *************************************************************************** * Display CPU statistics in JSON format. * * IN: * @tab Number of tabs to print. * @g_itv Interval value in jiffies multiplied by the number of CPU. * @prev Position in array where statistics used as reference are. * Stats used as reference may be the previous ones read, or * the very first ones when calculating the average. * @curr Position in array where current statistics will be saved. *************************************************************************** */ void write_json_cpu_stats(int tab, unsigned long long g_itv, int prev, int curr) { struct stats_cpu *scc, *scp; unsigned long long pc_itv; int cpu, next = FALSE; xprintf(tab++, "\"cpu-load\": ["); /* Check if we want global stats among all proc */ if (*cpu_bitmap & 1) { next = TRUE; xprintf0(tab, "{\"cpu\": \"all\", \"usr\": %.2f, \"nice\": %.2f, \"sys\": %.2f, " "\"iowait\": %.2f, \"irq\": %.2f, \"soft\": %.2f, \"steal\": %.2f, " "\"guest\": %.2f, \"gnice\": %.2f, \"idle\": %.2f}", (st_cpu[curr]->cpu_user - st_cpu[curr]->cpu_guest) < ⭐ (st_cpu[prev]->cpu_user - st_cpu[prev]->cpu_guest) ? ⭐ 0.0 : ll_sp_value(st_cpu[prev]->cpu_user - st_cpu[prev]->cpu_guest, ⭐ st_cpu[curr]->cpu_user - st_cpu[curr]->cpu_guest, ⭐ g_itv),
感想
- ソースを読んだら意外な事実が発覚することが Linux カーネルでは時折ある
- ところで KVM で VM を動かしてないなら /proc/stat の guest は増えないはずなので別に支障はない
/proc/stat
を読み取っても guest を捨てて計算しておいたら支障はないsar
は %guest を表示しない。どうやって計算しているんだろ?
root@test001:~# sar 1 Linux 4.4.0-66-generic (001) 10/11/17 _x86_64_ (48 CPU) 12:56:35 CPU %user %nice %system %iowait %steal %idle 12:56:36 all 18.37 0.00 4.31 0.19 0.00 77.13 12:56:37 all 29.69 0.00 5.02 0.32 0.00 64.98 12:56:38 all 24.02 0.00 4.08 0.38 0.00 71.52 12:56:39 all 31.86 0.00 3.62 0.29 0.00 64.22
エントリを書いたモチベーション
いつもお世話になっている GitHub の mackrelio/mackerel-agent にほぼ同様の内容で issue 報告していたのでした
mackerel に閉じた話題ではないので、 issue とは別の場所でもまとめておいたほうがいいだろうと思い本エントリを記した