動機
sar -uで出力される値は平均なのか?瞬間なのか?
どこみてものってなかったので、とりあえずソースコードを追ってみることにしました。
結論から言えば、平均値です。
ソースコードの入手
sourceforgeからsysstat(sarを含むツール群)のソースコードを入手できます。↓
http://sourceforge.jp/projects/freshmeat_sysstat/
ソースコードリーディング
まずは、pr_stats.cから。
これにprint_cpu_statsという関数があります。
名前からして表示部分っぽそうですね。
実際に
printf("\n%-11s CPU %%user %%nice %%system"
" %%iowait %%steal %%idle\n",
timestamp[!curr]);
という部分がありこれはsar -uのヘッダ部分なのでここで間違いなさそうです。
ということでこの関数の実装を読み進めていきます。
scc = (struct stats_cpu *) ((char *) a->buf[curr] + i * a->msize);
scp = (struct stats_cpu *) ((char *) a->buf[prev] + i * a->msize);
という部分がありました。ここはあとで重要なので
ちょい詳しく見てみます。まず構造体struct stats_cpuですが、
これはrd_stats.hで定義されていてCPUの使った時間の情報を入れる構造体のようです。
定義は以下。
/*
* Structure for CPU statistics. * In activity buffer: First structure is for global CPU utilisation ("all"). * Following structures are for each individual CPU (0, 1, etc.)
*/
struct stats_cpu {
unsigned long long cpu_user __attribute__ ((aligned (16)));
unsigned long long cpu_nice __attribute__ ((aligned (16)));
unsigned long long cpu_sys __attribute__ ((aligned (16)));
unsigned long long cpu_idle __attribute__ ((aligned (16)));
unsigned long long cpu_iowait __attribute__ ((aligned (16)));
unsigned long long cpu_steal __attribute__ ((aligned (16)));
unsigned long long cpu_hardirq __attribute__ ((aligned (16)));
unsigned long long cpu_softirq __attribute__ ((aligned (16)));
unsigned long long cpu_guest __attribute__ ((aligned (16)));
unsigned long long cpu_guest_nice __attribute__ ((aligned (16)));
};
cpu_sysとかidleとかの情報が入っていてこれが計算の基本になりそうな感じですね。
さてsccとscpのところにもどってみると
a->buf[curr]
a->buf[prev]
という部分があります。currとprevは引数としてわたっていて、今見ているデータ、一つ前のデータの格納されているインデックスのようです。
ということでなにやらポインタ演算していますがそれを無視してもこの部分では、今と一つ前のCPUの情報を取得してsccとscpに入れていると解釈できそうです。
さらに読み進めていくと
if (DISPLAY_CPU_DEF(a->opt_flags)) {
printf(" %6.2f %6.2f %6.2f %6.2f %6.2f %6.2f\n",
ll_sp_value(scp->cpu_user, scc->cpu_user, g_itv),
ll_sp_value(scp->cpu_nice, scc->cpu_nice, g_itv),
ll_sp_value(scp->cpu_sys + scp->cpu_hardirq + scp->cpu_softirq,
scc->cpu_sys + scc->cpu_hardirq + scc->cpu_softirq,
g_itv),
ll_sp_value(scp->cpu_iowait, scc->cpu_iowait, g_itv),
ll_sp_value(scp->cpu_steal, scc->cpu_steal, g_itv),
scc->cpu_idle < scp->cpu_idle ?
0.0 :
ll_sp_value(scp->cpu_idle, scc->cpu_idle, g_itv));
}
という部分があります。if文の条件式が何を意味しているのか読まなくても、
printfのフィールドの数,ll_sp_valueという関数の引数からここでデータを表示していると考えて間違いなさそうです。
ll_sp_valueがなにやら重要そうですね。ってことでこれの実装を読んでいきます。
common.cにあります。
double ll_sp_value(unsigned long long value1, unsigned long long value2,
unsigned long long itv)
{
if ((value2 < value1) && (value1 <= 0xffffffff))
/* Counter's type was unsigned long and has overflown */
return ((double) ((value2 - value1) & 0xffffffff)) / itv * 100;
else
return SP_VALUE(value1, value2, itv);
}
オーバフロー対策らしい実装はわかりませんが基本的に第一引数で渡されたものと第二引数で渡された引数を引き算してそれをg_itvでわり、100をかけて%標記にしている模様です。( SP_VALUEも動揺のマクロ)
じゃあこのg_itvってなんでしょう?
get_intervalという関数が存在し、どうやら今と一つ前のuptimeの差(tick単位)を求めてるっぽいです。g_itvはこの結果とみて間違いないでしょう。ちなみにuptimeはread_stat_cpu関数(rd_stats.c)で計算され一部抜粋しますと
*uptime = st_cpu->cpu_user + st_cpu->cpu_nice +
st_cpu->cpu_sys + st_cpu->cpu_idle +
st_cpu->cpu_iowait + st_cpu->cpu_hardirq +
st_cpu->cpu_steal + st_cpu->cpu_softirq;
と、各和として定義されています(後述しますがこの各値は/proc/statから取られているので起動時からの積算です)
書くのが面倒になってきたので詳細は省きますが、(おい)cpu_userのデータは/proc/statから取られこれは起動してからの積算値になっているので、
つまり、今と一つ前の値をひいているということはインターバル間に消費したCPU時間を取得し、それを全体が消費したCPU時間で割るということで
今と一つ前の平均ということになるわけですね。
ちなみに省いた部分についてはsadcとかのソース、/proc/statの出力の意味とか調べると出てきます。