理解iowait

[TOC]

什么是iowait?

顾名思义,就是系统因为io导致的进程wait。再深一点讲就是:这时候系统在做io,导致没有进程在干活,cpu在执行idle进程空转,所以说iowait的产生要满足两个条件,一是进程在等io,二是等io时没有进程可运行。

Iowait是如何计算的?

先说说用户如何看到iowait吧
我们通常用vmstat就能看到iowat

这个数据是vmstat经过计算文件/proc/stat中的数据获得,所以说大家看到的是能够大概反应一个系统iowait水平的数据表象。关于/proc/stat中的数据都代表了什么意思,大家自己google吧,不再赘述。

那/proc/stat文件中的这些数据是从哪来的呢?

Kernel中有个proc_misc.c文件会专门输出这些数据,这个文件对应的函数是show_stat
部分代码:

for_each_possible_cpu(i) {
        int j;

        user = cputime64_add(user, kstat_cpu(i).cpustat.user);
        nice = cputime64_add(nice, kstat_cpu(i).cpustat.nice);
        system = cputime64_add(system, kstat_cpu(i).cpustat.system);
        idle = cputime64_add(idle, kstat_cpu(i).cpustat.idle);
        iowait = cputime64_add(iowait, kstat_cpu(i).cpustat.iowait);
        irq = cputime64_add(irq, kstat_cpu(i).cpustat.irq);
        softirq = cputime64_add(softirq, kstat_cpu(i).cpustat.softirq);
        steal = cputime64_add(steal, kstat_cpu(i).cpustat.steal);
        for (j = 0 ; j < NR_IRQS ; j++)
            sum += kstat_cpu(i).irqs[j];
    }
….
    seq_printf(p,
        "\nctxt %llu\n"
        "btime %lu\n"
        "processes %lu\n"
        "procs_running %lu\n"
        "procs_blocked %lu\n",
        nr_context_switches(),
        (unsigned long)jif,
        total_forks,
        nr_running(),
        nr_iowait());
…

这部分代码会输出你在/proc/stat中看到的数据,通过代码我们得知iowait来自

iowait = cputime64_add(iowait, kstat_cpu(i).cpustat.iowait);

那么 cpustat.iowait是谁来修改的呢?

我们找到了这个函数account_system_time

void account_system_time(struct task_struct *p, int hardirq_offset,
             cputime_t cputime)
{
    struct cpu_usage_stat *cpustat = &kstat_this_cpu.cpustat;
    struct rq *rq = this_rq();//在smp环境下获取当前的run queue
    cputime64_t tmp;

    p->stime = cputime_add(p->stime, cputime);

    /* Add system time to cpustat. */
    tmp = cputime_to_cputime64(cputime);
    if (hardirq_count() - hardirq_offset)//在做硬中断
        cpustat->irq = cputime64_add(cpustat->irq, tmp);
    else if (softirq_count())//在做软中断
        cpustat->softirq = cputime64_add(cpustat->softirq, tmp);
    else if (p != rq->idle)//程序在正常运行,非idle
        cpustat->system = cputime64_add(cpustat->system, tmp);
    else if (atomic_read(&rq->nr_iowait) > 0)//既不做中断,而且在idle,那么就是iowait
        cpustat->iowait = cputime64_add(cpustat->iowait, tmp);
    else
        cpustat->idle = cputime64_add(cpustat->idle, tmp);
    /* Account for system time used */
    acct_update_integrals(p);
}

我们可以看出,当某个cpu产生iowait时,那么这个cpu上肯定有进程在进行io,并且在等待io完成(rq->nr_iowait>0),并且这个cpu上没有进程可运行(p == rq->idle),cpu在idle。

谁在产生iowait?

那么是谁修改了rq->nr_iowait呢?
重点终于来了

void __sched io_schedule(void)
{
    struct rq *rq = &__raw_get_cpu_var(runqueues);

    delayacct_blkio_start();
    atomic_inc(&rq->nr_iowait);
    schedule();
    atomic_dec(&rq->nr_iowait);
    delayacct_blkio_end();
}

long __sched io_schedule_timeout(long timeout)
{
    struct rq *rq = &__raw_get_cpu_var(runqueues);
    long ret;

    delayacct_blkio_start();
    atomic_inc(&rq->nr_iowait);
    ret = schedule_timeout(timeout);
    atomic_dec(&rq->nr_iowait);
    delayacct_blkio_end();
    return ret;
}

所以产生iowait的根源被我们找到了,就是函数io_schedule, io_schedule_timeout,顾名思义,这两个函数是用来做进程切换的,而且切换的原因是有io。只不过io_schedule_timeout还给出了一个sleep的时间,也就是timeout。

Iowait的具体含义

Reports the percentage of time the processor(s) were idle during which the system had outstanding disk/NFS I/O
request(s).

也即iowait其实是一种特殊形式的CPU空闲。特殊之处在于,在此CPU的等待队列上有线程在等待IO完成(我们称之为pendingIO 或者 outstanding IO)。

这是由 IO 的特点决定的,因为 IO 速度较慢,现代操作系统实现 IO 一般是通过异步中断来完成的:即提交 IO 请求,然后线程挂起进入等待队列;IO 完成后,再通过中断通知相关线程转到就绪队列,进行处理。

在相关任务线程提交完 IO 请求,到 IO 中断返回的过程中,此时 IO 主要由存储侧处理,主机侧 CPU 实际上处于空闲状态。如果此时有其他任务线程可调度,系统会直接调度其他线程,这样 CPU 就相应显示为Usr 或 Sys;但是如果此时系统较空闲,无其他任务可以调度,CPU 就会显示为 iowait(实际上与 idle 无本质区别)。

注意 AIX仅仅标记那些触发未完成 IO 任务的空闲CPU为 iowait 状态,不会牵连到系统中其他空闲的CPU(这些CPU 状态依然标记为 IDLE 空闲状态)。这样就有效减少了一部分 iowait 值虚高的情形:比如一个 4 颗物理 CPU 的系统,如果只有其中一颗物理 CPU 上有未完成 IO 请求,则 iowait 最高不会超过 25%.

iowait 的一些事实

  1. %iowait 合理值取决于应用 IO 特点。
    比如备份任务往往 iowait 较高;而 cache 命中率高、磁盘读写少的应用负载 iowait 一般不高。

  2. 从上述说明可以看到,减少%iowait 的方法有两类:
    一类是进一步缩减 IO 处理时间,比如采用 SSD 盘,或者甚至内存盘等技术;
    另外一类是缩减 IO 处理过程中 CPU 的空闲时间,比如在系统中添加 CPU 密集型任务,可以使得%iowait 比例明显降低甚至
    为 0;

  3. %iowait 比例与是否存在 IO 性能问题并无直接关系:
    低 iowait 也不代表没有磁盘性能问题;参考第二点,完全可能在实际上 IO 服务时间非常长,但由于系统中同时存在 CPU 密集型任务掩盖了 iowait。

高 iowait 不一定代表有磁盘性能问题;因为系统可能比较空闲,而业务类型是 IO 密集型比如备份。

你可能感兴趣的:(理解iowait)