VMSTATで表示される各時間の詳細を調べてみた

## 概要
vmstatコマンドなどで`ユーザー時間`、`システム時間`、`IO要求待ち時間`、`アイドル時間`に相当する項目を見てシステムのボトルネックを調査したりしますが、そもそもこの値ってどうやってカーネルで計測しているんだろう〜と思っていました。そんな時に`サーバー/インフラを支える技術`という本にこの疑問に答えてくれる記述があり、その箇所について自分なりに深掘りしたときのメモです。

`全体的に知識不足なので内容に関して間違っていること激高です。その場合はご容赦下さい。`

## 読んだ本
[[24時間365日] サーバ/インフラを支える技術 ‾スケーラビリティ、ハイパフォーマンス、省力運用 (WEB+DB PRESS plusシリーズ)](https://www.amazon.co.jp/24%E6%99%82%E9%96%93365%E6%97%A5-%E3%82%A4%E3%83%B3%E3%83%95%E3%83%A9%E3%82%92%E6%94%AF%E3%81%88%E3%82%8B%E6%8A%80%E8%A1%93-%E3%82%B9%E3%82%B1%E3%83%BC%E3%83%A9%E3%83%93%E3%83%AA%E3%83%86%E3%82%A3-%E3%83%8F%E3%82%A4%E3%83%91%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%B3%E3%82%B9-%E7%9C%81%E5%8A%9B%E9%81%8B%E7%94%A8-PRESS-plus%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA/dp/4774135666/ref=sr_1_1?ie=UTF8&qid=1484807600&sr=8-1&keywords=%E3%82%A4%E3%83%B3%E3%83%95%E3%83%A9)

## 環境
```
$ uname -r
2.6.32-642.6.2.el6.x86_64

$ cat /etc/redhat-release 
CentOS release 6.8 (Final)
```

自分の環境が上記なので、とりあえずstableリポジトリにあった2.6.32の`2.6.32.71`を対象に深掘りしてみました。

## プロセスアカウンティング

本によるとカーネルは各プロセスが生成されてからどの程度CPU時間を利用したかをプロセスごとに記録しているそうです。そしてその記録している処理を`プロセスアカウンティング`と呼んでいて、その始まりが`kernel/timer.c`の中にある`update_process_times()`ということでした。

各プロセスの`ユーザー時間`、`システム時間`、`IO要求待ち時間`、`アイドル時間`は`update_process_times()`から始まる処理で統計が取られており、vmstatなどのコマンドはこの情報を取得して表示している前提で進めます。

## update_process_times()
それでは上記の環境で`update_process_times()`を探してみます。

```sh
$ grep -rn update_process_times ./kernel
./kernel/timer.c:1208:void update_process_times(int user_tick)
./kernel/tags:10861:update_process_times        timer.c /^void update_process_times(int user_tick)$/;"  f
./kernel/time/tick-common.c:72: update_process_times(user_mode(get_irq_regs()));
./kernel/time/tick-sched.c:559:  * time we slept as update_process_times does only a 1 tick
./kernel/time/tick-sched.c:628: update_process_times(user_mode(regs));
./kernel/time/tick-sched.c:788:         update_process_times(user_mode(regs));
```
本と同じく`kernel/timer.c`の中にあるようです。

調査したカーネルが本のカーネルのバージョンと違うので多少コードが違っています。(本ではカーネルバージョンは書かれてなかったです。たぶん。)気にせずにupdate_process_times()を見てみます。


```kernel/timer.c
/*
 * Called from the timer interrupt handler to charge one tick to the current
 * process.  user_tick is 1 if the tick is user time, 0 for system.
 */
void update_process_times(int user_tick)
{
        struct task_struct *p = current;
        int cpu = smp_processor_id();

        /* Note: this timer irq context must be accounted for as well. */
        account_process_tick(p, user_tick);
        run_local_timers();
        rcu_check_callbacks(cpu, user_tick);
        printk_tick();
        scheduler_tick();
        run_posix_cpu_timers(p);
}
```

account_process_tick()が本でも紹介されている`プロセスアカウンティング`の中心のようです。account_process_tick()をみていきます。

## account_process_tick()

```kernel/shced.c
 /*
 * Account a single tick of cpu time.
 * @p: the process that the cpu time gets accounted to
 * @user_tick: indicates if the tick is a user or a system tick
 */
void account_process_tick(struct task_struct *p, int user_tick)
{
        cputime_t one_jiffy_scaled = cputime_to_scaled(cputime_one_jiffy);
        struct rq *rq = this_rq();

        if (user_tick)
                account_user_time(p, cputime_one_jiffy, one_jiffy_scaled);
        else if ((p != rq->idle) || (irq_count() != HARDIRQ_OFFSET))
                account_system_time(p, HARDIRQ_OFFSET, cputime_one_jiffy,
                                    one_jiffy_scaled);
        else
                account_idle_time(cputime_one_jiffy);
}
```

### 前提
* this_rq() 
  - ローカルCPUの実行キューのアドレスを取得します。取得した実行キューには`TASK_RUNNNING`のプロセスしか存在しない。よって、IO待ちで待機しているプロセスやスリープしているプロセスはこの実行キューには存在しない。ただし、例外としてswapperプロセス(idleプロセス)は存在する。

* rq->idle
  - ローカルCPUのswapperプロセスのプロセスディスクリプタへのポインタ

* swapperプロセス
  - アイドルプロセスのこと
  - [swapperプロセスとは](http://qiita.com/saikoro-steak/items/e8121afdd5f294e14c43)
  - カレントプロセスがswapperプロセスということはthis_rq()で取得するランキュー上には`TASK_RUNNNING`のプロセスは存在しない。


ここで`ユーザー時間`、`システム時間`、`アイドル時間(IO要求待ち時間含む)`の各処理に分岐します。どのような条件でプロセスの時間が割り当てられ統計が取られていくのかを見ていきます。

## ユーザー時間の統計
`user_tick`は直近のプロセスが動いている時間がユーザー時間だったのか、システム時間であったのかを判定するフラグのようです。これがユーザー時間のフラグであれば、account_user_time()の中でユーザー時間として計測されます。


## システム時間の統計
システム時間として計測される条件

```kernel/shced.c
        else if ((p != rq->idle) || (irq_count() != HARDIRQ_OFFSET))
                account_system_time(p, HARDIRQ_OFFSET, cputime_one_jiffy,
                                    one_jiffy_scaled);
```

### ① p != rq->idle
```c
p != rq->idle
```

カレントプロセスがswapperプロセス(idleプロセス)でない場合にtrue。直近の時間がユーザ時間ではなくてカレントプロセスが`TASK_RUNNNING`なプロセスの場合はシステム時間として計測されるようです。

#### ② irq_count() != HARDIRQ_OFFSET
本では記載が無かった条件`(irq_count() != HARDIRQ_OFFSET)`が追加されています。

気になったのでgitで該当箇所のコミットを探してコミットメッセージを見てみました。

```diff
$ git show f5f293a4
commit f5f293a4e3d0a0c52cec31de6762c95050156516
Author: Eric Dumazet <dada1@cosmosbay.com>
Date:   Wed Apr 29 14:44:49 2009 +0200

    sched: account system time properly
    
    Andrew Gallatin reported that IRQ and SOFTIRQ times were
    sometime not reported correctly on recent kernels, and even
    bisected to commit 457533a7d3402d1d91fbc125c8bd1bd16dcd3cd4
    ([PATCH] fix scaled & unscaled cputime accounting) as the first
    bad commit.
    
    Further analysis pointed that commit
    79741dd35713ff4f6fd0eafd59fa94e8a4ba922d ([PATCH] idle cputime
    accounting) was the real cause of the problem.
    
    account_process_tick() was not taking into account timer IRQ
    interrupting the idle task servicing a hard or soft irq.
    
    On mostly idle cpu, irqs were thus not accounted and top or
    mpstat could tell user/admin that cpu was 100 % idle, 0.00 %
    irq, 0.00 % softirq, while it was not.
    
    [ Impact: fix occasionally incorrect CPU statistics in top/mpstat ]
    
    Reported-by: Andrew Gallatin <gallatin@myri.com>
    Re-reported-by: Andrew Morton <akpm@linux-foundation.org>
    Signed-off-by: Eric Dumazet <dada1@cosmosbay.com>
    Acked-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
    Cc: rick.jones2@hp.com
    Cc: brice@myri.com
    Cc: Paul Mackerras <paulus@samba.org>
    Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
    LKML-Reference: <49F84BC1.7080602@cosmosbay.com>
    Signed-off-by: Ingo Molnar <mingo@elte.hu>

diff --git a/kernel/sched.c b/kernel/sched.c
index b902e58..26efa47 100644
--- a/kernel/sched.c
+++ b/kernel/sched.c
@@ -4732,7 +4732,7 @@ void account_process_tick(struct task_struct *p, int user_tick)
 
        if (user_tick)
                account_user_time(p, one_jiffy, one_jiffy_scaled);
-       else if (p != rq->idle)
+       else if ((p != rq->idle) || (irq_count() != HARDIRQ_OFFSET))
                account_system_time(p, HARDIRQ_OFFSET, one_jiffy,
                                    one_jiffy_scaled);
        else
```

恐らく、コミットメッセージから推測するにaccount_process_tick()はタイマ割り込みがswapperプロセスを中断するときを考慮していなかったようです。account_system_time()の先で割り込みに関する情報も足し込んでおりこの条件を入れることでswapperプロセスの場合でも割り込みの時間を考慮するようになるのではないでしょうか。

account_system_time()全体

```kernel/shced.c
/*
 * Account system cpu time to a process.
 * @p: the process that the cpu time gets accounted to
 * @hardirq_offset: the offset to subtract from hardirq_count()
 * @cputime: the cpu time spent in kernel space since the last update
 * @cputime_scaled: cputime scaled by cpu frequency
 */
void account_system_time(struct task_struct *p, int hardirq_offset,
                         cputime_t cputime, cputime_t cputime_scaled)
{
        struct cpu_usage_stat *cpustat = &kstat_this_cpu.cpustat;
        cputime64_t tmp;

・
・
・
        /* Add system time to cpustat. */
        tmp = cputime_to_cputime64(cputime);
        if (hardirq_count() - hardirq_offset)
                cpustat->irq = cputime64_add(cpustat->irq, tmp);
        else if (in_serving_softirq())
                cpustat->softirq = cputime64_add(cpustat->softirq, tmp);
        else
                cpustat->system = cputime64_add(cpustat->system, tmp);

        cpuacct_update_stats(p, CPUACCT_STAT_SYSTEM, cputime);

        /* Account for system time used */
        acct_update_integrals(p);
}
```
割り込み処理時間を考慮する必要がある場合には割り込み時間に足しこんで、その必要が無く`TASK_RUNNNING`なプロセスの場合はシステム時間として計測されるようです。


## I/O wait時間、idle時間の統計
account_process_tick()に戻って見てみます。

```kernel/shced.c
 /*
 * Account a single tick of cpu time.
 * @p: the process that the cpu time gets accounted to
 * @user_tick: indicates if the tick is a user or a system tick
 */
void account_process_tick(struct task_struct *p, int user_tick)
{
        cputime_t one_jiffy_scaled = cputime_to_scaled(cputime_one_jiffy);
        struct rq *rq = this_rq();

        if (user_tick)
                account_user_time(p, cputime_one_jiffy, one_jiffy_scaled);
        else if ((p != rq->idle) || (irq_count() != HARDIRQ_OFFSET))
                account_system_time(p, HARDIRQ_OFFSET, cputime_one_jiffy,
                                    one_jiffy_scaled);
        else
                account_idle_time(cputime_one_jiffy);
}
```
account_idle_time()を呼び出す時のカレントプロセスはswapperプロセスになるかと思われます。ということは、カレントプロセスがユーザープロセスではなく、swapperプロセスであり、割り込み処理に関する考慮が必要でない場合にaccount_idle_time()に処理を移します。account_idle_time()を見ていきます。


### account_idle_time()

```kernel/shced.c
/*
 * Account for idle time.
 * @cputime: the cpu time spent in idle wait
 */
void account_idle_time(cputime_t cputime)
{
        struct cpu_usage_stat *cpustat = &kstat_this_cpu.cpustat;
        cputime64_t cputime64 = cputime_to_cputime64(cputime);
        struct rq *rq = this_rq();

        if (atomic_read(&rq->nr_iowait) > 0)
                cpustat->iowait = cputime64_add(cpustat->iowait, cputime64);
        else
                cpustat->idle = cputime64_add(cpustat->idle, cputime64);
}
```

#### 前提
* &rq->nr_iowait
  - もともとこの実行キューリスト上にいて、現在はディスクI/O操作の完了を待っているプロセス数のこと

## I/O wait時間の統計
カレントプロセスがswapperプロセスの場合に(`TASK_RUNNNING`なプロセスが存在しない場合)、IO待ちのプロセスが一つでも存在すればIOwait時間として足し込まれます。

## idle時間の統計
カレントプロセスがswapperプロセスの場合(`TASK_RUNNNING`なプロセスが存在しない場合)に、IOwaitのプロセスが一つでも存在しない場合、idle時間として足し込まれます。

# 色々試してみる
調べた結果とコマンドの挙動が感覚的にあっているか知りたかったので色々試してみました。

## 前提
以下で各プロセス状態を作成する。

R(TASK_RUNNING): phpで無限ループを実行

```test.php
<?php
while(true){
}
```

```bash
USER       PID   LWP %CPU NLWP %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root      9123  9123 94.6    1  0.7 210204  7148 pts/3    R+   00:01   0:18 php test.php
```

D(TASK_UNINTERRUPTIB): `dd if=/dev/zero of=test bs=100M count=512`

```bash
root      8091  8091  0.0    1  0.0      0     0 ?        D    Jan19   0:00 [flush-253:1]
root      9528  9528  6.8    1 10.1 207596 103120 pts/0   D+   00:03   0:01 dd if=/dev/zero of=test bs=100M count=512
```

この２つを用いて意図的にプロセス状態R(TASK_RUNNING)とD(TASK_UNINTERRUPTIB)を作って色々試してみる。


## D(TASK_UNINTERRUPTIB)プロセスあり

何もしていない状態からD(TASK_UNINTERRUPTIB)なプロセスを増やしてみます。

ddコマンド実行

```bash
dd if=/dev/zero of=test bs=100M count=512
```

別の端末でVMSTAT実行確認

```bash
procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 0  0      0    187      7    728    0    0     0     0  860   31  0  0 100  0  0 
 0  0      0    187      7    728    0    0     0     0  832   34  1  1 98  0  0 
 0  2      0    579      7    238    0    0     8 61760 1105   87  0 22 71  6  1 
 1  2      0    465      7    347    0    0     0 122184 1111  265  1  9  0 90  0 
 0  2      0    356      7    452    0    0     0 116940  855  283  0  8  0 92  0 
 2  2      0    294      7    519    0    0     0 67544  605  148  0 10  0 90  0 
 0  2      0    149      7    653    0    0     0 126236  606  157  1 15  0 82  1 
 1  2      0     72      7    728    0    0     0 113608  969  316  0  9  0 91  0 
 0  4      0     72      7    728    0    0   224 98280  878  429  0  8  0 91  1 
 0  2      0     73      7    726    0    0   196 83668 1076  470  1 10  0 88  1
```
```bash
USER       PID   LWP %CPU NLWP %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root     11795 11795  3.1    1  0.0      0     0 ?        D    02:47   0:01 [flush-253:1]
root     11821 11821 10.4    1 10.1 207596 103124 pts/0   D+   02:48   0:02 dd if=/dev/zero of=test bs=100M count=512
```

Dの状態が増えたタイミングで、idleが0になり、waの値が増えました。これは「カレントプロセスがswapperプロセスの場合に(`TASK_RUNNNING`なRなプロセスが存在しない場合)、IO待ちのプロセスが一つでも存在すればIOwait時間として足し込まれる」に合致します。idle時間に足し込まれなくなり、IOwait時間に足しこまれたんだと思います。

## R(TASK_RUNNING)なプロセスあり
何もしていない状態からR(TASK_RUNNING)なプロセスを増やしてみます。

phpで無限ループを実行

```test.php
<?php
while(true){
}
```

```bash
USER       PID   LWP %CPU NLWP %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root     31265 31265 66.8    1  0.7 210204  7148 pts/3    R+   04:15   0:04 php test.php
```

```bash
procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 0  0      0    186      7    728    0    0     1    75  164    5  0  0 100  0  0 
 0  0      0    186      7    728    0    0     0     0   56   31  1  0 99  0  0 
 0  0      0    186      7    728    0    0     0     0   52   34  0  1 99  0  0 
 0  0      0    186      7    728    0    0     0     0   41   33  1  0 99  0  0 
 0  0      0    186      7    728    0    0     0     0   53   38  0  1 99  0  0 
 0  0      0    186      7    728    0    0     0     0   50   30  0  1 99  0  0 
 1  0      0    182      7    730    0    0  1772     0  486  133 38  0 23 38  0 
 1  0      0    181      7    730    0    0     0     0 1018   46 99  1  0  0  0 
 1  0      0    181      7    730    0    0     0     0 1017   35 100  0  0  0  0 
 1  0      0    181      7    730    0    0     0     0 1014   39 99  1  0  0  0 
 1  0      0    181      7    730    0    0     0     0 1016   37 100  0  0  0  0 
 1  0      0    182      7    730    0    0     0     0 1015   37 99  1  0  0  0 
 1  0      0    182      7    730    0    0     0    44 1021   50 100  0  0  0  0 
 1  0      0    182      7    730    0    0     0     0 1013   37 100  0  0  0  0 
・
・
・
 1  0      0    182      7    730    0    0     0     0 1016   39 99  1  0  0  0 
 1  0      0    182      7    730    0    0     0     0 1020   36 100  0  0  0  0 
 1  0      0    181      7    730    0    0     0     0 1013   38 99  1  0  0  0 
 1  0      0    181      7    730    0    0     0     0 1015   36 99  1  0  0  0 
 1  0      0    181      7    730    0    0     0     0 1017   36 99  0  0  0  1 
 1  0      0    181      7    730    0    0     0    36 1017   41 99  1  0  0  0 
 1  0      0    181      7    730    0    0     0     0 1015   37 99  0  0  0  1 
 0  0      0    185      7    730    0    0     0     0  629   44 60  1 39  0  0 
 0  0      0    185      7    730    0    0     0     0   47   35  1  0 99  0  0 
procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 0  0      0    185      7    730    0    0     0     0   51   36  0  1 99  0  0 
 0  0      0    185      7    730    0    0     0     0   52   32  1  1 98  0  0 
 0  0      0    184      7    730    0    0     0     0   46   31  0  0 100  0  0 
 0  0      0    184      7    730    0    0     0     0   44   30  0  1 99  0  0 
 0  0      0    184      7    730    0    0     0     0   49   29  0  1 99  0  0 
 0  0      0    184      7    730    0    0     0     0   45   34  1  0 99  0  0 
```

phpを実行したタイミング(Rプロセス生成)でidleは0％になりました。そのタイミングからユーザー時間に足し込まれれていると思います。プログラムはは無限ループなのでpsコマンドではずっとRの状態で確認できます。プログラムの実行を中断するとidleが100%になります。ユーザー空間で完結しているのでusの値が高くなっています。

## R(TASK_RUNNING)なプロセスとD(TASK_UNINTERRUPTIB)なプロセス混在

最後に R(TASK_RUNNING)とD(TASK_UNINTERRUPTIB)を混在させます。
初めにDなプロセスを生成して、次にRなプロセスを生成します。

RとDを確認

```bash
USER       PID   LWP %CPU NLWP %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root       452   452  0.0    1  0.0      0     0 ?        D    Jan17   0:01 [jbd2/dm-1-8]
root     32549 32549  1.7    1  0.0      0     0 ?        D    04:24   0:00 [flush-253:1]
root     32640 32640  5.2    1 10.1 207596 103124 pts/0   D+   04:25   0:01 dd if=/dev/zero of=test bs=100root     32681 32681 66.8    1  0.7 210204  7148 pts/3    R+   04:25   0:12 php test.php
```

何もない状態からDなプロセスを生成したタイミングでwaの値が増えます。Rがなく、DがあるのでIOwait時間に足し込まれるからです。次に、この状態でRなプロセスを生成するとwaの値は減り、usの値が増えます。waの値が減るのはDなプロセス(IO要求待ち)が存在しても、Rなプロセスがあるとユーザー時間かシステム時間に足し込まれるからです。これも調査した結果と合致します。IO中なのでioの値はそのまま遷移しています。

```bash
procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 0  0      0    187      7    727    0    0     1    78  166    5  0  0 100  0  0 
 0  0      0    187      7    727    0    0     0     0  727   34  0  1 99  0  0 
 0  0      0    187      7    727    0    0     0     0  875   36  1  1 97  0  1 
 0  0      0    187      7    727    0    0     0    28  802   45  0  0 90 10  0 
 0  0      0    187      7    727    0    0     0     0  692   31  0  2 98  0  0 
 0  0      0    187      7    727    0    0     0     0  752   33  0  1 99  0  0 
 0  0      0    187      7    727    0    0     0     0  884   32  1  1 97  0  1 
 0  0      0    187      7    727    0    0     0     0  684   36  0  0 100  0  0 
 0  0      0    187      7    727    0    0     0     0  794   32  0  1 99  0  0 
 0  0      0    187      7    727    0    0     0     0  631   35  1  0 99  0  0 
 1  0      0    228      7    687    0    0    72     0  671   56  0  1 96  2  1 
 0  2      0    517      7    296    0    0     0 133880 1106  186  0 27  0 73  0 
 0  2      0    430      7    380    0    0     4 93456 1019  201  0  8  0 92  0 
 0  1      0    367      7    442    0    0     4 59500  934  179  1  5  0 93  1 
 0  2      0    280      7    526    0    0     4 93388 1069  156  0  7  0 93  0 
 0  2      0    175      7    627    0    0     4 98800  717  151  1  9  0 90  0 
 0  3      0     95      7    707    0    0     0 69020  775  242  0  8  0 92  0 
 0  4      0     73      7    726    0    0   104 103228  805  242  0  8  0 91  1 
 0  2      0     71      7    730    0    0   316 82656  926  372  0  9  0 91  0 
 0  2      0     73      7    727    0    0     0 103824 1002  259  1  8  0 91  0 
 0  2      0     72      7    727    0    0     0 121272  972  273  0 10  0 90  0 
 0  3      0     65      7    735    0    0     0 53964 1098  265  0  5  0 94  1 
 0  4      0     69      7    731    0    0   416 94752 1066  542  1  7  0 92  0 
procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 0  3      0     73      7    728    0    0   536 64520 1131  367  0  7  0 92  1 
 0  3      0     69      7    729    0    0   576 106344 1041  377  0  8  0 92  0 
 1  5      0     70      7    726    0    0   796 83664  947  354  3  8  0 88  1 
 1  3      0     62      7    735    0    0   104 74624 1980  305 93  7  0  0  0 
 1  3      0     71      7    727    0    0     0 97272 1644  286 89 11  0  0  0 
 1  2      0     72      7    726    0    0     0 107860 1693  226 91  9  0  0  0 
 1  2      0     73      7    724    0    0     0 121980 1915  203 90 10  0  0  0 
 1  2      0     70      7    727    0    0     0 111384 2049  215 86 14  0  0  0 
 2  2      0     70      7    727    0    0     0 79348 1922  275 92  8  0  0  0 
 2  2      0     66      7    731    0    0    72 106128 1584  425 86 14  0  0  0 
 1  3      0     73      7    724    0    0   316 36428 1550  230 96  4  0  0  0 
 1  2      0     65      7    732    0    0    32 87696 1568  337 92  8  0  0  0 
 1  2      0     74      7    723    0    0     0 58968 1660  184 95  5  0  0  0 
 1  1      0     70      7    727    0    0     0 19656 1615  100 96  4  0  0  0 
 1  3      0     65      7    732    0    0     0 71100 1986  293 92  8  0  0  0 
 1  3      0     73      7    725    0    0     0 35296 1695  202 95  5  0  0  0 
 1  3      0     65      7    732    0    0     0 104836 1623  220 92  8  0  0  0 
 1  2      0     72      7    726    0    0     0 105840 1642  232 90 10  0  0  0 
 1  1      0     75      7    724    0    0     0 66284 1795  164 90 10  0  0  0 
 1  3      0     65      7    732    0    0    72 103108 1812  517 88 12  0  0  0 
 1  4      0     69      7    727    0    0   316 68544 1689  297 92  8  0  0  0 
 0  2      0     75      7    725    0    0    32 48956 1966  243 95  4  0  1  0 
 0  2      0     74      7    727    0    0     0 49528  928 7399  1  3  0 95  1 
procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 0  2      0     73      7    727    0    0     0 40824  834 6039  0  3  0 97  0 
 0  2      0     80      7    727    0    0     0  8608 1067  117  0  1  0 99  0 
 0  0      0    187      7    727    0    0     0  3596  774   67  1  0 36 63  0 
 0  0      0    187      7    727    0    0     0     0  886   32  1  1 97  0  1 
 0  0      0    187      7    727    0    0     0     0  824   33  0  0 100  0  0 
 0  0      0    187      7    727    0    0     0     0 1054   34  1  0 99  0  0 
 0  0      0    187      7    727    0    0     0     0 1140   33  0  1 98  0  1 
 0  0      0    187      7    727    0    0     0    24 1045   44  1  0 95  4  0 
 0  0      0    187      7    727    0    0     0     0  682   32  0  1 99  0  0 
 0  0      0    187      7    727    0    0     0     0  783   32  0  1 99  0  0 
 0  0      0    187      7    727    0    0     0     0  946   35  1  0 99  0  0
```

## 考察
* IO待ちが多い
  - vmstatコマンドでみるとwaが高い状況です(bも高くなる)。この状況は他にR(TASK_RUNNING)なプロセスが存在せずCPUが空いている状況で、D(TASK_UNINTERRUPTIB)プロセスが存在しているということです。何かしらシステムに不具合がある場合はIOがボトルネックになっている場合があるはずです。ただ、CPUは空いている状態なのでIOwaitが98%であってもCPUの負荷が高い状態ではないといえるはずです。
  - ただし、RなプロセスがCPUを使用するとwa(IO時間)は減少しますが、IO待ちのプロセスが無くなったわけではない。
* idle時間が多い
  - vmstatコマンドでみるとidleが高い状況です。R(TASK_RUNNING)なプロセスなし、D(TASK_UNINTERRUPTIB)プロセスもない、CPUが空いている状況を示しています。スリープしているプロセスがたくさんいるんだと思います。
* CPU負荷が高い
  - us + syの値が100%近く。syの方が高いとIOがボトルネックになっている場合もある。
  - LAが高いということ
      - R(TASK_RUNNING)とD(TASK_UNINTERRUPTIB)のプロセスが通常より多くいる
  - この状況下で
      - R(TASK_RUNNING)が多い場合、D(TASK_UNINTERRUPTIB)プロセスがたくさん存在していてもwaには反映されない。~~が、不具合がない限りRの状態がずっと続くことは少なそうなのでDがたくさんいた場合は、waに反映されそう~~。なのでこの場合、IO待ちの具合を調べるのはb列を確認するのが確実そう。ただ、waの値が高い状態は、そのまま単純にIO待ちが多いということを表している。
      - D(TASK_UNINTERRUPTIB)に比べてR(TASK_RUNNING)が多い場合usとsyが高いはず。
      - R(TASK_RUNNING)に比べてD(TASK_UNINTERRUPTIB)が多い場合、LA高いけど、waは低い、usとsyもそれなり。
      - CPUの負荷がかかっていない(us + syの値が100%近く無い)のにロードアベレージが上昇している場合には、I/Oがボトルネックになっている場合がある(まぁwaの値などを見ればよいのか)。

処理内容を実際にコードレベルで垣間見ることで考えに根拠を持つことができました。

# 最後に
日頃からカーネルがどういった動きをしているのか興味があり、疑問に思ったことから実際に自分なりにカーネルを追ってみました。分からないことはとても多いですがこれからも興味あるところから少しずつ読んでみたい思います。そしてこの経験がいつかどこかに繋がっていけば・・。

# 参考
* http://d.hatena.ne.jp/naoya/20070222
* http://d.hatena.ne.jp/yohei-a/20120201/1328108666
* [詳解 Linuxカーネル 第3版](https://www.amazon.co.jp/%E8%A9%B3%E8%A7%A3-Linux%E3%82%AB%E3%83%BC%E3%83%8D%E3%83%AB-%E7%AC%AC3%E7%89%88-Daniel-Bovet/dp/487311313X/ref=sr_1_2?s=books&ie=UTF8&qid=1484983113&sr=1-2&keywords=%E3%82%AB%E3%83%BC%E3%83%8D%E3%83%AB)