我对系统“ iowait”的基本假设不成立


13

我的基本假设是,当进程的唯一限制因素是磁盘和CPU时,总的系统“ iowait” + CPU使用率应至少等于一个逻辑CPU的100%。(在其他情况下,这将不成立。例如,使用来下载文件时wget,网络通常是限制因素)。

一个简单的测试违反了这一假设。这是预期的吗?如果可以预期,是否有一些条件可以期望我的假设成立?

这里有一些有关“ iowait”的背景:CPU如何知道有IO待处理? 这里的答案引用了违反直觉的想法,即累积的iowait“在某些情况下可能会减少”。我想知道我的简单测试是否会触发这种无证的情况?

更新:请跳到答案

答案比我最初使用的答案更简单。我保留了下面的原始问题。原始问题可能会显示一些其他详细信息。

原始问题

在简短测试中,我用于dd请求内核生成随机字节,并将其写入文件。我在dd内部运行命令perf stat,只是为了计算内核内部花费的CPU时间。我还在内部运行它perf trace -s,以报告在内部花费的时间write()。同时,我vmstat 5在另一个终端上运行,以查看系统“ iowait”。

  1. 我希望至少将整个CPU视为“非空闲”状态,即100%的时间它正在运行或已暂停但正在等待IO(“ iowait”状态)。不是。
  2. (此外,我期望看到“ iowait”时间大致与在write()中花费的时间相匹配。但是似乎没有这样做。)

详细结果和测试环境如下所示。还显示了替代测试,我的假设成立了。注意:有必要在perf stat内部运行perf trace,而不是相反。在此进行详细说明:运行“ perf trace-s”时,“ perf stat”(和“ time”!)显示的结果不正确吗?

关于“ iowait”的背景信息

以下是手册sar页中的定义:

%iowait:

在系统有未完成的磁盘I / O请求的过程中,一个或多个CPU空闲的时间百分比。

因此,%iowait意味着从CPU的角度来看,没有可运行的任务,但是至少有一个I / O正在运行。iowait只是空闲时间的一种形式,无法安排任何时间。该值在指示性能问题上可能有用也可能没有用,但是它确实告诉用户系统处于空闲状态,并且可能需要进行更多工作。

https://support.hpe.com/hpsc/doc/public/display?docId=c02783994

还有更长的文章:了解I / O等待(或为什么可以选择0%Idle)。这说明了如何从内核代码清楚地看到定义。代码有所更改,但思路仍然很明确:

/*
 * Account for idle time.
 * @cputime: the CPU time spent in idle wait
 */
void account_idle_time(u64 cputime)
{
    u64 *cpustat = kcpustat_this_cpu->cpustat;
    struct rq *rq = this_rq();

    if (atomic_read(&rq->nr_iowait) > 0)
        cpustat[CPUTIME_IOWAIT] += cputime;
    else
        cpustat[CPUTIME_IDLE] += cputime;
}

本文还显示了在单CPU系统上的许多相关实验。有些实验甚至ddif=/dev/urandom !但是实验不包括我的测试dd if=/dev/urandom of=test.out 。它仅使用dd if=/dev/urandom of=/dev/null 。

现在考虑“ IO等待”要棘手一些,因为我们使用的是多CPU系统,但是我仍然基于引用的代码理解它。

环境

我有四个逻辑CPU。

我使用LVM和ext4文件系统。我没有在磁盘或文件系统上使用任何加密。我根本没有安装任何网络文件系统,因此我没有读写网络文件系统。

以下结果来自内核4.20.15-200.fc29.x86_64,使用noopIO调度程序。该cfqIO调度也给出了类似的结果。

(在基于类似配置的内核构建上,我也看到了类似的结果,但更接近于内核版本5.1,并使用mq-deadline。因此使用的是新blk-mq代码)。

测试与结果

$ sudo perf trace -s \
       perf stat \
       dd if=/dev/urandom of=test.out bs=1M oflag=direct count=3000

3000+0 records in
3000+0 records out
3145728000 bytes (3.1 GB, 2.9 GiB) copied, 31.397 s, 100 MB/s

 Performance counter stats for 'dd if=/dev/urandom of=test.out bs=1M oflag=direct count=3000':

         18,014.26 msec task-clock                #    0.574 CPUs utilized          
             3,199      context-switches          #    0.178 K/sec                  
                 4      cpu-migrations            #    0.000 K/sec                  
               328      page-faults               #    0.018 K/sec                  
    45,232,163,658      cycles                    #    2.511 GHz                    
    74,538,278,379      instructions              #    1.65  insn per cycle         
     4,372,725,344      branches                  #  242.737 M/sec                  
         4,650,429      branch-misses             #    0.11% of all branches        

      31.398466725 seconds time elapsed

       0.006966000 seconds user
      17.910332000 seconds sys

 Summary of events:
...
 dd (4620), 12156 events, 12.0%

   syscall            calls    total       min       avg       max      stddev
                               (msec)    (msec)    (msec)    (msec)        (%)
   --------------- -------- --------- --------- --------- ---------     ------
   read                3007 17624.985     0.002     5.861    12.345      0.21%
   write               3003 13722.837     0.004     4.570   179.928      2.63%
   openat                12     0.371     0.002     0.031     0.267     70.36%
...

iowait从的wa列中读取了该图vmstat。您可以通过查看io列(bo= 1K块输出)来判断测试何时运行。

$ vmstat 5
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 5126892 176512 1486060   0   0  1788  4072  321  414  4  4 83  9  0
 1  0      0 5126632 176520 1485988   0   0     0     7  212  405  0  1 99  0  0
 0  0      0 5126884 176520 1485988   0   0     0     0  130  283  0  0 99  0  0
 0  0      0 5126948 176520 1485908   0   0     0     1  157  325  0  0 99  0  0
 0  0      0 5126412 176520 1486412   0   0   115     0  141  284  0  0 99  0  0
 0  2      0 5115724 176548 1487056   0   0     0  6019 18737 10733  3  6 89  2  0
 1  0      0 5115708 176580 1487104   0   0     3 91840 1276  990  0 13 77  9  0
 1  0      0 5115204 176600 1487128   0   0     2 91382 1382 1014  0 14 81  4  0
 1  0      0 5115268 176636 1487084   0   0     4 88281 1257  901  0 14 83  3  0
 0  1      0 5113504 177028 1487764   0   0    77 92596 1374 1111  0 15 83  2  0
 1  0      0 5114008 177036 1487768   0   0     0 113282 1460 1060  0 16 81  2  0
 1  0      0 5113472 177044 1487792   0   0     0 110821 1489 1118  0 16 74 10  0
 0  0      0 5123852 177068 1487896   0   0     0 20537  631  714  1  3 94  2  0
 0  0      0 5123852 177076 1487856   0   0     0    10  324  529  2  1 98  0  0
 2  0      0 5123852 177084 1487872   0   0     0    70  150  299  0  0 99  0  0

测试结果(虚拟机内部)

我在具有1个CPU的VM内尝试了相同的测试,该VM正在运行内核5.0.9-301.fc30.x86_64并使用mq-deadline(因此使用blk-mq)。在此测试中,它按我预期的那样工作。

$ sudo perf trace -s \
       perf stat \
       dd if=/dev/urandom of=test.out bs=1M oflag=direct count=3000
[sudo] password for alan-sysop:
3000+0 records in
3000+0 records out
3145728000 bytes (3.1 GB, 2.9 GiB) copied, 46.8071 s, 67.2 MB/s

 Performance counter stats for 'dd if=/dev/urandom of=test.out bs=1M oflag=direct count=3000':

         18,734.89 msec task-clock                #    0.400 CPUs utilized
            16,690      context-switches          #    0.891 K/sec
                 0      cpu-migrations            #    0.000 K/sec
               328      page-faults               #    0.018 K/sec
   <not supported>      cycles
   <not supported>      instructions
   <not supported>      branches
   <not supported>      branch-misses

      46.820355993 seconds time elapsed

       0.011840000 seconds user
      18.531449000 seconds sys


 Summary of events:
...
 dd (1492), 12156 events, 38.4%

   syscall            calls    total       min       avg       max      stddev
                               (msec)    (msec)    (msec)    (msec)        (%)
   --------------- -------- --------- --------- --------- ---------     ------
   write               3003 28269.070     0.019     9.414  5764.657     22.39%
   read                3007 18371.469     0.013     6.110    14.848      0.53%
   execve                 6    10.399     0.012     1.733    10.328     99.18%
...

输出vmstat 5

$ vmstat 5
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 726176  52128 498508    0    0  2040   231  236  731  7  5 77 11  0                                                                     
 0  0     0 726176  52136 498508    0    0     0    10   25   46  0  0 99  1  0                                                                     
 0  0     0 726208  52136 498508    0    0     0     0   29   56  0  0 100  0  0                                                                    
 0  1     0 702280  55944 511780    0    0  2260 13109 4399 9049  3 17 55 25  0                                                                     
 0  1     0 701776  56040 511960    0    0    18 129582 1406 1458 0 73  0 27  0                                                                    
 0  2     0 701524  56156 512168    0    0    22 87060  960  991  0 50  0 50  0                                                                     
 3  1     0 701524  56228 512328    0    0    14 118170 1301 1322 0 68  0 32  0                                                                    
 1  1     0 701272  56260 512392    0    0     6 86426  994  982  0 53  0 46  0                                                                     
 0  2     0 701020  56292 512456    0    0     6 56115  683  660  0 37  0 63  0                                                                     
 3  2     0 700540  56316 512504    0    0     5 33450  446  457  0 26  0 74  0                                                                     
 0  2     0 700860  56332 512536    0    0     3 16998  311  240  0 19  0 81  0                                                                     
 1  2     0 700668  56368 512616    0    0     7 32563  443  428  0 24  0 76  0                                                                     
 1  0     0 700668  56392 512648    0    0     3 20338  245  272  0 12  0 88  0                                                                   
 0  1     0 707096  56408 512920    0    0    54 20913  312  530  0 12 79  8  0                                                                     
 0  0     0 707064  56432 512920    0    0     0    49   39   64  0  0 45 55  0                                                                     
 0  0     0 707064  56432 512920    0    0     0     0   24   46  0  0 100  0  0                                                                    
 0  0     0 707064  56432 512920    0    0     0    80   28   47  0  0 100  0  0

我尝试将CPU热添加到VM并再次进行测试。结果是可变的:有时在空闲列中显示为0%,有时显示约50%空闲(即,两个CPU中的一个)。在“空闲”为0%的情况下,“ iowait”非常高,即一个CPU的价值超过一个。即我的期望点2是不正确的。我可以接受在多CP​​U系统上这种 “ iowait”的明显限制。(尽管我不太了解它。如果有人想确切地解释它,那将很棒)。但是,无论哪种情况,“空闲”都不会超过50%,因此这些测试仍然与我对“ iowait”的第一个假设相一致。

我尝试关闭VM,并以4个CPU启动它。同样,通常我有75%的空闲时间,有时我只有50%的空闲时间,但是我看不到超过75%的空闲时间(即,四个CPU中有三个以上)。

在具有4个CPU的物理系统上,我仍然可以重现80%以上空闲状态的结果,如上所示。


您介意稍微说明一下您的两个期望吗?您能否添加实际价值是大于还是小于您的期望。我知道这是在原始数据中,它将更具可读性。对于您为什么期望1 cpu(100%),我还不清楚。根据您的链接之一和引用的内核代码,单个IO操作会将所有IDLE时间切换为IOWAIT时间(所有4个内核-400%)。
菲利普·库林

@PhilipCouling“我希望我至少将整个CPU视为“非空闲”……不是。” 空闲时间比预期的要长,我将此归咎于iowait时间比预期的要少。在内核代码中,我认为仅是当前CPU上this_rq()->nr_iowait正在等待使用的任务数。我错了吗?io_schedule()
sourcejedi

1
我一点也不确定,但是我发现确实如此。这种惊讶似乎与斯蒂芬·基特(Stephen Kitt)的回答相吻合,他说:iowait一般来说,它试图衡量等待I / O所花费的时间。它不是由特定的CPU跟踪的,也不是。” 让我强调一点,我不确定,只是表示惊讶。
菲利普·库林

@PhilipCouling如果运行atopatopsar -c 5,则将看到每个CPU的使用情况数字。它们包括iowait,每个CPU的iowait数字可以显示不同的非零值:-)。或者sar -P ALL 1,如果您不使用atop。这是该iowait模型已扩展到多CPU系统的方式...我不清楚的是该模型是否实际可用,还是只有当有一个CPU时,这种方式才能使iowait代码继续工作在线,但否则不值得信赖。
sourcejedi

Answers:


7

内容通知:这篇文章包括各种Linux讨论和代码的链接。某些链接的内容不符合当前适用于StackExchangeLinux的行为准则。大多数情况下,他们“侮辱代码[而不是人]”。但是使用了某种语言,不应重复使用。我要求您避免模仿,模仿或辩论此类语言。


回复:iowait与闲置记帐“不一致”-iowait太低

Peter Zijlstra在05/07/2019 12:38写道:

艾伦·詹金斯(Alan Jenkins)在2019年7月5日星期五12:25:46 PM +0100写道:

我的CPU“ iowait”时间似乎报告不正确。你知道为什么会这样吗?

因为iowait是一个无理的魔术随机数。就个人而言,我宁愿只删除整个内容,除了ABI:/

另请参见nr_iowait()附近的评论

谢谢。我认为[当前文档中提到的问题]是不同的问题,但是您的意思是“解决”我的问题的需求不大(或毫无意义)。

我发现了问题。它已经在五年前被注意到,并且修复起来并不容易。

“ iowait”时间由函数更新account_idle_time()

/*
 * Account for idle time.
 * @cputime: the CPU time spent in idle wait
 */
void account_idle_time(u64 cputime)
{
    u64 *cpustat = kcpustat_this_cpu->cpustat;
    struct rq *rq = this_rq();

    if (atomic_read(&rq->nr_iowait) > 0)
        cpustat[CPUTIME_IOWAIT] += cputime;
    else
        cpustat[CPUTIME_IDLE] += cputime;
}

如果您要通过使用传统计时器中断(“滴答”)进行“采样” 来近似估算CPU时间,这将达到我的预期。但是,如果在空闲时间关闭对勾以节省电量,则可能无法工作NO_HZ_IDLE。如果由于性能原因而允许关闭刻度线,它也可能会失败NO_HZ_FULL--因为那需要启动VIRT_CPU_ACCOUNTING。大多数Linux内核都使用节能功能。某些嵌入式系统不使用这两个功能。这是我的解释:

IO完成后,设备将发送一个中断。内核中断处理程序使用唤醒进程try_to_wake_up()。它从nr_iowait计数器中减去一个:

if (p->in_iowait) {
    delayacct_blkio_end(p);
    atomic_dec(&task_rq(p)->nr_iowait);
}

如果在空闲的CPU上唤醒了该进程,则该CPU调用account_idle_time()。根据所应用的配置,这称为tick_nohz_account_idle_ticks()from __tick_nohz_idle_restart_tick()vtime_task_switch()from finish_task_switch()

到这个时候,->nr_iowait已经减少了。如果减少为零,则不会记录任何等待时间。

这种效果可能会有所不同:它取决于唤醒该进程的CPU。如果在接收到IO完成中断的同一CPU上唤醒该进程,则可以在->nr_iowait减少之前先考虑空闲时间。就我而言,我发现CPU 0 通过查看来处理ahci中断watch cat /proc/interrupts

我通过简单的顺序读取来测试了这一点:

dd if=largefile iflag=direct bs=1M of=/dev/null

如果使用将该命令固定到CPU 0 taskset -c 0 ...,则会看到iowait的“正确”值。如果将其固定到其他CPU,则看到的值会低得多。如果我正常运行该命令,则该命令将根据调度程序的性能而有所不同,调度程序的性能在内核版本之间有所不同。在最新的内核(4.17、5.1、5.2-rc5-ish)中,该命令似乎在CPU 0上花费了大约1/4的时间,因为“ iowait”时间减少到该比例。

(未解释:为什么现在在我的虚拟机上运行此测试似乎可以为每个(或任何一个)CPU再现“正确的” iowait。我怀疑这可能涉及IRQ_TIME_ACCOUNTING,尽管在VM之外的测试中也使用了此功能。

我也没有确切确认为什么抑制NO_HZ_IDLE会为4.17+上的每个CPU提供“正确的” iowait,而不是在4.16或4.15上。

对于每个(或任何)CPU,在我的虚拟机上运行此测试似乎可以重现“正确的” iowait。这是由于 IRQ_TIME_ACCOUNTING。它也在VM外部的测试中使用,但是在VM内部进行测试时会遇到更多中断。具体来说,运行“ dd”的虚拟CPU上每秒有1000多个“功能调用中断”。

因此,您不应过多地依赖我的解释的详细信息:-)

这里有一些有关“ iowait”的背景:CPU如何知道有IO待处理? 这里的答案引用了违反直觉的想法,即累积的iowait“在某些情况下可能会减少”。我想知道我的简单测试是否会触发这种无证的情况?

是。

当我第一次查找时,发现有“打ic”的话题。同样,通过显示累积的“ iowait”时间是非单调的来说明该问题。那就是它有时会向后跳(减少)。它不像上面的测试那样简单。

但是,当他们调查时,发现了相同的根本问题。分别由Peter Zijlstra和Hidetoshi Seto提出并提出了解决方案原型。封面消息中解释了该问题:

[RFC PATCH 0/8]返工iowait会计(2014-07-07)

除此以外,我没有发现任何进展的证据。关于其中一个细节有一个未解决的问题。此外,整个系列还涉及PowerPC,S390和IA64 CPU体系结构的特定代码。因此,我说这并非易事。


2
您是否可以确认或拒绝(使用vmstat):无论启用或禁用的空闲状态如何,内核4.15都可以实现您所期望的;无论如何,内核4.16并没有达到您的期望。vmstat似乎使用/proc/stat,但是我使用/sys/devices/system/cpu/cpu*/cpuidle/state*/usage,据我所知,它一直都是准确的(+-几%)。我无法在较旧的内核上使用我的工具,因为那里没有一些新信息。请注意,我希望TEST1和TEST3给予同样的结果,因为蜱永远不会停止在空闲状态0
道格Smythies

1
我的意思是在/sys/devices/system/cpu/cpu*/cpuidle/state*/time上面写。我只能考虑将内核一分为二,一次在内核4.15和4.16之间进行,然后再次在4.16和4.17之间进行。从第一部分获得的知识,第二部分可能会更快。我可能没有几天时间才有时间做。
道格·史密斯

1
@DougSmythies谢​​谢!您的测试效果和我原来的一样好。我的结果4.15.0-1.fc28,并4.16.0-300.fc28同意你的。
sourcejedi '19

好吧,我想我已经准备好接受linux-pm list了。希望有人会有所见识,并且我们可以避免内核二分法。
道格·史密斯

1
@DougSmythies wtf。第一部分(4.15-4.16)给出了github.com/torvalds/linux/commit/806486c377e3 “已调度 /正常:如果prev_cpu处于空闲状态,请勿迁移”。因此,我taskset -c 0在v4.15上进行了测试...使用运行dd命令taskset -c 2给出了“正确的” iowait。固定到任何其他CPU都会产生“错误的” iowait。dd如果不使用cpu2的话将会结束taskset。(我atop以前看过每CPU的iowait时间)。我正在看第二部分,以解释当前的行为。在第二次更改中可能对此发表了一些评论。
sourcejedi
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.