当前位置: 首页 > Linux

朱辉(茶水):LinuxKerneliowaittime的代码原理

时间:2023-04-07 00:10:12 Linux

本文转载,版权归作者所有。商业转载请联系作者授权,非商业转载请注明出处。作者:朱辉(茶水)来源:微信公众号linux代码阅读领域(id:linuxdev)作者介绍朱辉,个人主页http://teawater.github.io/,微信公众号茶水看山(cschatcs)。做了几年模拟器,几年GDB,几年在小米电视做Linux内核优化,主要围绕MM。现在在HyperHQ担任软件工程师。2017.12.15更新记录:细化了扩展文章的问题描述。2017.12.10:根据张晓、宋宝华的建议修改最后的错误。添加扩展阅读。添加了CPU负载平衡问题的说明。之前在我最喜欢的公众号Linux代码阅读栏目看到这篇文章TheprecisemeaningofI/OwaittimeinLinux。感觉文章写的不错,但是如果没有在源码中实现,感觉有点晦涩难懂,所以自己看代码。当任务iowait发生时,内核通过将任务切出来处理它们,让可运行的任务先运行,在切出之前,将其in_iowait设置为1,再次唤醒时,将in_iowait设置为original价值。相关函数io_schedule、io_schedule_timeout、mutex_lock_io、mutex_lock_io_nested。例如:可以看出in_iowait表示任务是否在iowait中。另外需要注意的是,除了mutex_lock_io和mutex_lock_io_nested之外,这些切换函数都会将任务运行状态设置为TASK_UNINTERRUPTIBLE,内核在调用io_schedule和io_schedule_timeout之前会将任务运行状态设置为TASK_UNINTERRUPTIBLE。进程切换函数__schedule在切换任务时,如果被切换出去的任务的in_iowait为真,则将本CPU运行队列rq结构体中的nr_iowait加1。因为前面的任务已经设置为TASK_UNINTERRUPTIBLE,需要唤醒任务,nr_iowait的归约操作也是在任务唤醒函数中完成的。可以看出nr_iowait可以表示某个CPU上iowait中是否有任务,以及数量。因为iowait中的任务处于TASK_UNINTERRUPTIBLE状态,不在就绪队列中,所以不可能被CPU负载均衡到其他CPU,所以nr_iowait不需要处理负载均衡问题。在累加系统空闲时间时,如果CPU的nr_iowait为真,即当前CPU有任务等待iowait,则记录为iowait时间。在开启了NO_HZ的内核中,相关代码在update_ts_time_stats中。未开通的是account_idle_time。当访问相关的/proc/stat接口时,get_iowait_time会访问这个时间并返回。总结一下,iowait时间就是CPU的空闲时间,但是这个时候,并不是CPU上没有TASK可以运行,而是休眠任务中有一个或者几个是iowait任务。当然在idle和iowait的时候CPU上还有空闲任务。最后推荐一篇阿里内核组的文章作为扩展阅读内核文档/newiowait计算(http://link.zhihu.com/?target...+aio_read_events(ctx,min_nr,nr,event,&ret),until);不管until的timeout值是多少,都会调用wait_event_interruptible_hrtimeout。hrtimer虽然实时性高,但是可以看到宏__wait_event_hrtimeout中的hrtimer使用实际处理wait的初始化使用:hrtimer_start_range_ns(&__t.timer,timeout,\current->timer_slack_ns,\HRTIMER_MODE_REL);\第三个参数current->timer_slack_ns是传给hrtimer的触发范围,因为hrtimer实时性高,但是频繁触发系统显然受不了,所以每次触发hrtimer,都会处理时间范围内的所有定时器(见__hrtimer_run_queues)。所以timeout+current->timer_slack_ns就是最后一次触发时间设置hrtimer,current->timer_slack_ns的默认值是50000,也就是50000纳秒。即这个时钟最长会在50000纳秒后触发,当然也有可能是前面的hrtimer触发的。所以在wait_event_interruptible_hrtimeout中,一旦ctx->wait没有准备好,即使设置timeout时间为0,也极有可能调用一次schedule,导致iowait时间相差较大,对性能也有很大伤害.并且这个问题也被5f785de588735306ec4d7c875caf9d28481c8b21修复了,这段代码改为:-wait_event_interruptible_hrtimeout(ctx->wait,-aio_read_events(ctx,min_nr,nr,event,&ret),until);+if(until.tv64==0)+aio_read_events(ctx,min_nr,nr,event,&ret);+else+wait_event_interruptible_hrtimeout(ctx->wait,+aio_read_events(ctx,min_nr,nr,event,&ret),+until);这样当until为0时,直接调用aio_read_events。应该没有更明显的iowait问题,并且此修复已将io_getevents调用的性能提高了一百倍以上。当然,iowait不够准确的原因还是存在的。一旦因需要而发生任务切换,仍然会存在不准确的问题。最后要吐槽一下aio的设计。如果我有aio还需要等吗?更多精彩更新来袭……欢迎关注微信公众号:linux代码阅读田(id:linuxdev)