朱辉:Linux Kernel iowait 时间的代码原理

之前在我热爱的公众号Linuxer看到The precise meaning of I/O wait time in Linux这篇文章,感觉写的不错,就是没有落实到源码上感觉稍微有点晦涩,于是自己读了一下代码。

当task发生iowait的时候,内核对他们的处理方法是将task切换出去,让可运行的task先运行,而在切换出去前,会将其in_iowait设置为1,再次被唤醒的时候in_iowait被设置为原值。相关函数io_schedule,io_schedule_timeout,mutex_lock_io,mutex_lock_io_nested。

例如:

由此可见in_iowait表明了这个task是否在iowait。

另外要注意的是,这几个切换函数除了mutex_lock_io,mutex_lock_io_nested会设置task运行状态为TASK_UNINTERRUPTIBLE外,内核在调用io_schedule,io_schedule_timeout前都会设置task运行状态TASK_UNINTERRUPTIBLE。

在进程切换函数__schedule在切换task的时候,如果被切换出的task的in_iowait为真,则会对这个CPU的运行队列rq结构中的nr_iowait加1。

因为前面对task已经被设置为TASK_UNINTERRUPTIBLE,则task需要被唤醒,对nr_iowait的减少操作也是在task唤醒函数来做的。

由此可见nr_iowait可以表明某CPU上是否有task在iowait,以及数量。

因为处于iowait的task是TASK_UNINTERRUPTIBLE状态,其并不在就绪队列中,所以其也没有被CPU负载均衡到其他CPU的可能,所以nr_iowait也不需要处理负载均衡问题。

当累加系统idle时间的时候,如果CPU的nr_iowait为真,也就是当前这个cpu有task在等待iowait,则记录为iowait时间。

在打开NO_HZ的内核中,相关代码在update_ts_time_stats。

而没打开的则在 account_idle_time。

当相关/proc/stat接口被访问时,get_iowait_time就会访问这个时间并返回。

综上所述,iowait时间就是CPU idle时间,但是这时候CPU上不是完全没TASK需要运行,而是休眠的task中有一个或者若干个是iowait的task。

当然idle和iowait的时候CPU上还有idle task。

最后推荐一篇阿里内核组的文章作为扩展阅读Kernel Documents/new iowait calculation (http://link.zhihu.com/?target=http%3A//kernel.taobao.org/index.php%3Ftitle%3DKernel_Documents/new_iowait_calculation)

比较有意思是这里:

+ wait_event_interruptible_hrtimeout(ctx->wait,

+ aio_read_events(ctx, min_nr, nr, event, &ret), until);

无论超时值until是什么值,都会调用wait_event_interruptible_hrtimeout,虽然是hrtimer实时性已经很高,但是在用来实际处理wait的宏__wait_event_hrtimeout可以看到hrtimer初始化使用的是:

hrtimer_start_range_ns(&__t.timer, timeout,\

current->timer_slack_ns,\

HRTIMER_MODE_REL);\

其中第三个参数current->timer_slack_ns是传递给hrtimer的触发范围,因为hrtimer实时性高,但是频繁触发系统显然受不了,所以每次hrtimer触发都会将时间范围内的timer都处理掉(见__hrtimer_run_queues)。所以timeout+current->timer_slack_ns才是设置的hrtimer的最后触发时间,current->timer_slack_ns的默认值是50000,也就是代表50000纳秒。也就是这个时钟最久会在50000纳秒后触发,当然也可能被之前的hrtimer触发。

所以在wait_event_interruptible_hrtimeout中,一旦ctx->wait未能就绪,即使设置超时时间为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不够精确的原因还是存在,一旦因为需要发生task切换,还是会有不够精确的问题。

最后要吐槽一下aio的设计,都aio了还需要wait吗?

本文来自企鹅号 - Linuxer媒体

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏zhisheng

0Day技术分析-1-基础知识

1 基础知识 本章介绍一些与0Day相关的基本概念及基础知识。 1.1. Bug与漏洞 有一个比较有趣的事件,话说某个软件中存在99个Bug,某一天研发人员心血...

33340
来自专栏vue学习

6.vue-router之命名路由和命名视图

① 官方文档的解释:https://router.vuejs.org/zh/guide/essentials/named-routes.html 就是在rou...

11710
来自专栏java达人

Kafka漫游记

我是一条消息,从我被生产者发布到topic的时候,我就清楚自己的使命:被消费者获取消费。但我一直很纳闷,把我直接推送给消费者不就行了,为什么一定要先推送到类似队...

27170
来自专栏Golang语言社区

【Go 语言社区】Web 通信 之 长连接、长轮询(long polling)--转

基于HTTP的长连接,是一种通过长轮询方式实现"服务器推"的技术,它弥补了HTTP简单的请求应答模式的不足,极大地增强了程序的实时性和交互性。 一、什么是长连接...

1.2K30
来自专栏Java成神之路

Web 通信 之 长连接、长轮询(long polling)

基于HTTP的长连接,是一种通过长轮询方式实现"服务器推"的技术,它弥补了HTTP简单的请求应答模式的不足,极大地增强了程序的实时性和交互性。

53130
来自专栏Java职业技术分享

分布式 | Dubbo 架构设计详解

Dubbo是Alibaba开源的分布式服务框架,它最大的特点是按照分层的方式来架构,使用这种方式可以使各个层之间解耦合(或者最大限度地松耦合)。从服务模型的角度...

28500
来自专栏linux驱动个人学习

Linux核心调度器之周期性调度器scheduler_tick--Linux进程的管理与调度(十八)

因而内核提供了两个调度器主调度器,周期性调度器,分别实现如上工作, 两者合在一起就组成了核心调度器(core scheduler), 也叫通用调度器(gener...

20120
来自专栏蓝天

三种web性能压力测试工具http_load webbench ab小结

题记:压力和性能测试工具很多,下文讨论的是我觉得比较容易上手,用的比较多的三种 http_load 下载地址:http://www.acme.com/sof...

22110
来自专栏前端儿

【转】http-equiv="X-UA-Compatible" 设置IE浏览器兼容模式详解

文件兼容性用于定义让IE如何编译你的网页。此文件解释文件兼容性,如何指定你网站的文件兼容性模式以及如何判断一个网页该使用的文件模式。

35410
来自专栏Android小菜鸡

用H5页面打开APP

  业务场景,一个分享出去的h5界面通过页面内某个事件的触发,启动目标app并执行相关逻辑处理或做其他页面跳转(如:跳应用市场下载应用等)。下面是我在企业开发过...

99110

扫码关注云+社区

领取腾讯云代金券