关闭

Home 欢迎加盟 关于我们 热门文章 搜索技术博客-淘宝关注技术 关注搜索 关注淘宝 graph database 性能优化 搜索引擎 前端技术 数据挖掘 导购搜索 搜索动态 分布式技术 其他 28

551人阅读 评论(0) 收藏 举报

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

Iowait是如何计算的?
先说说用户如何看到iowait吧
我们通常用vmstat就能看到iowat,图中的wa就是(标红)

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

那/proc/stat文件中的这些数据是从哪来的呢?
Kernel中有个proc_misc.c文件会专门输出这些数据,这个文件对应的函数是show_stat
部分代码:

这部分代码会输出你在/proc/stat中看到的数据,通过代码我们得知iowait来自
iowait = cputime64_add(iowait, kstat_cpu(i).cpustat.iowait);

那么 cpustat.iowait是谁来修改的呢?
我们找到了这个函数account_system_time

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

谁在产生iowait?
那么是谁修改了rq->nr_iowait呢?
重点终于来了,呵呵。

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

systemtap来跟一下到底是谁在什么时候调用了这两个函数?
在这里我们以引擎为例子,trace进程searcher_server

Stap脚本Block.stp:(只截取了部分程序)

程序的大意就是在1S内,统计哪个进程分别调用了多少次这两个函数。并且把调用时的堆栈print出来,这样能更清楚地看到到底是哪个系统调用跑到了这个地方。

在最正常的状态下,跑一下机器:

此时新 引擎 searcher QPS有1500+,cpu busy有88%,iowait几乎为0,内存在mmap时全部用MAP_LOCKED被锁在内存中

1
2
3
4
5
6
7
8
9
10
11
sudo stap block.stp pid 5739 -DMAXSKIPPED=1000000
Fri Jul  6 05:57:21 2012 average schedule times:0
Fri Jul  6 05:57:22 2012 running...
Fri Jul  6 05:57:22 2012 average schedule times:0
Fri Jul  6 05:57:23 2012 running...
Fri Jul  6 05:57:23 2012 average schedule times:0
Fri Jul  6 05:57:24 2012 running...
Fri Jul  6 05:57:24 2012 average schedule times:0
Fri Jul  6 05:57:25 2012 running...
Fri Jul  6 05:57:25 2012 average schedule times:0

跑了一会发现并没有调用到io schedule,这也符合我们的预期。
我们再一边跑dd一边stap抓取

sudo stap block.stp pid 5739 -DMAXSKIPPED=1000000 > directdd
起两个dd进程,写10G的数据,不走page cache,direct写
dd if=/dev/zero of=a count=20000000 oflag=direct
dd if=/dev/zero of=b count=20000000 oflag=direct
一共写20G

  • Searcher表现:

    Cpu busy & iowait

    Latency:

    可以看出direct dd产生的iowait极小,最高才1.4左右,但是对searcher却也造成了不小的影响,通过vmstat的结果来看,当执行dd之后进程上下文切换从2W+飙到了8W+,被block的searcher线程为个位数。

  • 被block的线程堆栈:

    写log

  • 被block的频率

    经stap追查发现,切换次数的增加都是由于direct dd导致的:
    由于是direct写,所以每写一次都要做io schedule

  • 小结:

    Searcher latency上升和searcher相对温和的io schedule、进程切换都有关系,但是这时的主因应该是进程切换,进程切换还会造成频繁的进程迁移,TLB flush ,Cache pollution。

    再做一次新的实验,把dd的direct flag去掉,让page cache生效
    Searcher的运行环境和运行压力和上同

  • Searcher表现:

    Cpu busy & iowait:

    Latency:

    可以看出带page cache的dd对searcher影响更大,我们先看一下vmstat抓取到的数据

    平均被block的线程数据很多,甚至在某个时刻可以运行的线程数量为0

  • searcher被block时的堆栈:

    block layer写请求

    1
    2
    3
    4
    5
    6
    7
    8
    trace time:Fri Jul  6 07:13:45 2012
     0xffffffff8006377c : io_schedule+0x1/0x67 [kernel]
     0xffffffff80028a90 : get_request_wait+0xd8/0x11f [kernel]
     0xffffffff8000bfff : __make_request+0x33d/0x401 [kernel]
     0xffffffff8001c049 : generic_make_request+0x211/0x228 [kernel]
     0xffffffff80033472 : submit_bio+0xe4/0xeb [kernel]
     0xffffffff8001a793 : submit_bh+0xf1/0x111 [kernel]
     0x00000ffffffff800

    Sync buffer

    此时的dirty ratio已大于40%,需要做blk_congestion_wait,这个可以算是最严厉的惩罚了。。

    Searcher用到的某些页被刷出去,需要sync page read

    1
    2
    3
    4
    5
    6
    7
    8
    trace time:Fri Jul  6 07:13:49 2012
     0xffffffff8006377c : io_schedule+0x1/0x67 [kernel]
     0xffffffff80028936 : sync_page+0x3e/0x43 [kernel]
     0xffffffff800638fe : __wait_on_bit_lock+0x36/0x66 [kernel]
     0xffffffff8003fbad : __lock_page+0x5e/0x64 [kernel]
     0xffffffff800139f8 : find_lock_page+0x69/0xa2 [kernel]
     0xffffffff800c45a5 : grab_cache_page_write_begin+0x2c/0x89 [kernel]
     0x00000ffffffff800
  • 被block的频率:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    searcher_server:21010=>io_schedule 3
    searcher_server:21003=>io_schedule 1
    Fri Jul  6 07:14:39 2012 running...
    searcher_server:21010=>io_schedule 7
    Fri Jul  6 07:14:40 2012 running...
    searcher_server:21008=>io_schedule 1
    Fri Jul  6 07:14:41 2012 running...
    Fri Jul  6 07:14:42 2012 running...
    searcher_server:21004=>io_schedule 1
    Fri Jul  6 07:14:43 2012 running...
    searcher_server:21014=>io_schedule 11
    searcher_server:21015=>io_schedule 1
    searcher_server:21008=>io_schedule 1
    Fri Jul  6 07:14:44 2012 running...
    Fri Jul  6 07:14:45 2012 running...
    Fri Jul  6 07:14:46 2012 running...
    searcher_server:21003=>io_schedule 2
    Fri Jul  6 07:14:47 2012 running...
    Fri Jul  6 07:14:48 2012 running...
    Fri Jul  6 07:14:49 2012 running...
  • 小结:

    当带page cahce进行dd时,很容易就能达到10%的background dirty ratio和40%的dirty ratio,达到40%的时候buffered write就变成了sync write。经stap trace发现每次blk_congestion_wait都要耗时100ms左右,也就是说一个线程要被block 100ms,很致命。

    为了减少io的影响,我们把log给禁掉
    再做一次实验

  • Searcher表现:

    Cpu busy & iowait

    Latency:

    把写log关掉之后竟然还有iowait,是谁造成的呢?

  • Searcher被block时的堆栈:

    我们的内存都被mlock了,竟然还有sync page,为啥呢?
    用blktrace和debugfs追了一下,发现竟然是一个算法数据的问题
    /path/of/data
    原来是这个文件的数据被dd给刷出去了,导致还要重新read到内存
    然后写了个程序把这个数据也lock到内存中
    ./lock /path/of/data

    Lock数据再重新跑dd with page cache

  • Searcher 表现:

    Cpu busy & iowait:

    Latency:

    可以看到,iowait水平又降低了不少,那么此时此刻,谁还在制造iowait呢?

  • Searcher被block时的堆栈:

    原来是内存很少了,导致申请内存时要走到try_to_free_pages(平时极少走到),走到这一步说明系统内存已经少的可怜。但是没办法,谁让searcher还要去malloc呢,这些malloc来自两部分:1,mempool申请的内存,其实这个是完全可以抹掉的 2,算法so中STL部分用到的内存。

  • 小结:

    关掉log,将数据都lock在内存中,降低了iowait的水平,但是要让searcher不受影响,还要做更多的工作,比如不申请内存。

    如何消除searcher(或应用系统)的iowait?
    1, 没有io
    不写log,或者把写log的事情交给一个专门线程来做,searcher不做buffered write;不做disk read,尤其是sync page这类操作。
    2, 全内存且不申请内存
    用到的数据read once,全内存且lock住;把mempool做到完美,起码做到99%的case不做内存申请。
    3, 尽量减少其他应用的io影响
    其实就是能将dd的负面影响降到最少,如用cgroup;在scp多个大文件的时候,在传输过程中及时清理每个大文件的page cache,将系统的dirty ratio维持在10%以下,尤其是不能达到40%。

    工具连接:
    Systemtap
    Blktrace+Blkparse+Debugfs 结合使用,会找到每次io对应的磁盘块所属inode,还可以查看块内容

  • 18 RESPONSES


    1. 宝牛 on 08 三 2013

      赞,好文章

    2. KO on 13 三 2013

      完全没有IO的计算有意义么? IO wait只是说明CPU 在Idle. 高低并不重要.

      • 子嘉 on 26 六 2013

        完全没有IO的计算有很多,如需要即时响应的搜索引擎,内存数据库等,对于这种类型的应用对IO的干扰会非常敏感,IO wait说明了一点就是cpu因为等待io而在idle,高低当然重要,无论是不是重io的系统,iowait都是一个值得去优化降低的数值,这个值越低系统会跑得越快越健康

    3. xzt on 20 五 2013

      如果IOwait 是idle,在iostat中,为什么没有将其计入idle

      • 子嘉 on 25 六 2013

        kernel将cpu归为3个状态:idle,iowait,busy;idle是真正的idle,没活可干,没有进程是runnale的,cpu空闲;iowait时cpu没法干活(需要的数据还没就绪呢,所以叫wait),这时候cpu也空闲;busy就是在干活。idle和iowait时虽然cpu都是空闲的,但是将这两个状态区分开是很有意义的,因为iowait可以衡量由io问题导致的cpu空闲。

        • justlinux2010 on 04 八 2013

          如果是异步IO的话,在等待IO操作完成的时候可以调度其他进程来执行,并不是没法干活吧。
          参考资料:http://serverfault.com/questions/12679/can-anyone-explain-precisely-what-iowait-is

          • 子嘉 on 07 八 2013

            问题就在于有些IO操作是没法异步的,比如mmap的文件访问时发生缺页中断,需要sync_page去读磁盘文件内容;在读写文件时读取文件系统meta data。有一个关键点是这时可运行的进程数量为0,和你上层用什么IO机制无关。不过在一个具体的应用中,sync write极大概率上会比buffered write产生的iowait高,这个sync write在kernel层面上更容易堵住所有进程,即使“完全不相干”的线程也会被堵住,所谓的完全不相干也是不可能的,比如只有程序A产生IO,程序B是个内存计算型的,A的IO操作也有可能把B给堵住,至于为什么无辜的B被波及,这是个更大的话题。不过一般传导函数都是handle_mm_fault。

            • 丁凯 on 02 九 2013

              Hi,看你这篇博客很有收获,目前我遇到的一个问题:使用网络文件系统时,后端卡住,导致客户端IOWait飙升。CPU在进行IO操作之前,是否应该将自己切换出运行队列呢?什么时候会等待IO而非将自己切换出去?另外,楼主你能否举个例子呢?比如内核中哪里有读写会一直阻塞等待而不是仅仅发起请求?

            • deng on 28 二 2014

              既然有些io没法异步,cpu在等待这些io时,已经无法干其他活了,为什么还要求:”有一个关键点是这时可运行的进程数量为0″?有没有runnable process,cpu都无法干活呀?

            • 子嘉 on 28 二 2014

              可运行进程为0是iowait存在的前提,所以我们讨论iowait这个指标就是衡量cpu没有干活的时间,然后用这个指标来指导系统优化。无论是轻io还是重io的应用,都存在很多方法来降低io对整个系统的影响,比如最简单的把小的追加写在应用层汇集成一个大的追加写就会明显降低iowait,对于一个应用来说把iowait控制得越低程序效率会越高,因为这意味着cpu和io做了很好的隔离,比如像写log这种行为有的程序员可能会把系统的iowait搞成10%,有的程序员采用各种优化措施系统的iowait完全可以为0,真正的功夫就在这里。现在的linux kernel的设计和实现方式就决定了cpu、内存、io是互相干扰的,工程师就是在了解实现细节和约束之后把程序的效率搞到极致。写这篇文章的目的就是提醒大家注意这个指标及其背后的含义。

          • 子嘉 on 07 八 2013

            而且异步IO和iowait不是一个层面的问题了,那个参考资料我很久之前就搜过,但是远非答案,如果要想深入了解这个东东,建议还是看kernel源代码

            • 西平 on 14 九 2013

              %wa是说这个CPU在idle,同时跟一般的idle不同的是,此CPU上的进程正在wait。

              如果此CPU上还有其他进程可运行,则会让其他进程运行。此blocked在IO上的进程会挂起,并不会妨碍其他任务的运行。

              当然,如果所有的其他进程要依赖于io的完成才能继续,则必须去优化iowait了。

          • alex1986 on 28 三 2014

            同步IO的话,cpu等待IO时,如果有其他进程可以执行,也是可以调度其他进程来执行的吧

            • 子嘉 on 28 三 2014

              可以的,比如单核机器有2个进程,一个一直在wait io,一个就是个简单的while(1)循环,这时iowait几乎就是0,但是如果没有那个while(1)的进程,iowait就接近100%

    4. 西平 on 14 九 2013

      此外,对下面这段的推论,似乎不太合理:

      “Searcher latency上升和searcher相对温和的io schedule、进程切换都有关系,但是这时的主因应该是进程切换,进程切换还会造成频繁的进程迁移,TLB flush ,Cache pollution。”

      从您的全文来看,应该是与io schedule是有很大关系的,这时做io的调度,并非进程切换吧。 此外后续的分析,似乎进程切换没有关系。

      请帮忙解惑。 Thanks。

      • 子嘉 on 11 三 2014

        进程切换成本大概平均是30us(微妙)一次,每秒8W的切换平均到每个核上(16核机器)是5000次每秒,耗费时间是5000*30us=150ms,也就是说有15%的时间耗费在进程切换上,这时候的iowait最高才1.4%,也就是说相比iowait此时进程切换带来的影响更大。其实你如果看到了io schedule的调用堆栈你就明白了,像A进程做direct io会直接调用io schedule,此时B进程受到的最大影响来自A不停切换(kernel会在不同的核上进行balance)导致的B的迁移和切换,如果A进程用buffer write来写,使用大量的page cache,B进程在需要内存时(page fault)会触发kernel内存回收,可能会走到io schedule这个函数,这时对B来说就很冤枉,A做io但是B来承担后果(当然A也会一直受到惩罚,做write时会不停io schedule)。

        • ydzhang on 24 六 2014

          嗨,嘉哥,你这里说的进程切换成本大概平均30us,这个是如何测量的?


    留言


    0
    0

    查看评论
    * 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
      个人资料
      • 访问:3065次
      • 积分:51
      • 等级:
      • 排名:千里之外
      • 原创:1篇
      • 转载:8篇
      • 译文:0篇
      • 评论:0条