源码技术
php
java
python
Linux
行业商讯
当前位置:
首页
>
Linux
>Linux 内核是如何计算 iowait 的
Linux 内核是如何计算 iowait 的
2026-10-21 13:27:19
Linux 内核是如何计算 iowait 的
上一篇文章我们介绍了如何正确理解 Linux 系统中的 iowait 指标,
在 Linux 系统中 iowait 是 CPU 空闲(idle)时间的一部分,表示 CPU 因为等待 I/O 操作(主要是块设备 I/O)完成所花时间
的
百分比,它并不是 I/O 本身占用的 CPU 时间,而是
CPU 因为等待 I/O 而空闲的时间占比
。
查看 iowait 的命令有很多,比如:
vmstat,iostat,mpstat,
这些命令都是通过读取 /proc/stat 伪文件中的原始 CPU 统计信息来计算 iowait 和其他 CPU 统计信息的
。
/proc/stat 中的数据完全来自 Linux 内核的内部计数器,由内核在运行时动态统计和更新。下面让我们深入 Linux 内核源代码层面,看看 iowait 具体是怎么来的
。
首先找到内核实现
/proc/stat 伪文件的地方,可以看到主要实现在函数 show_stat 中。
函数
show_stat 获取每个 CPU iowait 时间的代码片段如下,可以看到每个 CPU 上都有一个 kernel_cpustat 结构,iowait 时间就记录在这个结构中,除此之外,这个结构中还记录了 CPU 花在 user mode,kernel mode,idle,irq 等方面的时间。用于从 kernel_cpustat 结构上获取 iowait 时间的函数为
get_iowait_time。
函数
get_iowait_time 的代码如下,它会调用函数
get_cpu_iowait_time_us 来获取以 us 为单位的 iowait 时间
。
我们继续看
get_cpu_iowait_time_us 的代码,该函数其实就是获取
per cpu 变量 tick_cpu_sched
中(
tick_sched 结构
)的
iowait_sleeptime 成员的值。
我们在内核中搜索
iowait_sleeptime,可以发现该成员的值是在函数
tick_nohz_stop_idle 中被更新的。函数
tick_nohz_stop_idle 是 Linux 内核中 NO_HZ 机制的一个关键函数,主要作用是停止 CPU 的空闲状态(退出 CPU 的空闲状态)并准备重新开始处理任务(调度进程)。
当 CPU 处于空闲状态(没有进程可运行)时,内核会停止周期性的时钟中断(
NO_HZ)以节省功耗,
当有进程被唤醒或中断发生时,CPU 需要退出这种深度空闲的状态,
tick_nohz_stop_idle 就是执行这个“退出空闲”操作的函数,而这个“退出空闲”的时间点正好可以用来更新 iowait 的时间,因为一段等待 I/O 的时间要想被算入 iowait 时间,必须要求 CPU 在这段时间是空闲状态的,那退出空闲的时间点最合适来更新这个 iowait 时间了。这个函数用当前时间 now 减去 CPU 进入空闲状态的时间 idle_entrytime,就得到了这次空闲的时间 delta,如果当前 CPU 的 nr_iowait_cpu 大于 0 的话,这段空闲时间 delta 就可以累加到
iowait_sleeptime 上了,否则这段空闲时间 delta 只能计入 idle
_sleeptime 了。所以,一段空闲的 CPU 时间片能否被计入
iowait 时间取决于函数
nr_iowait_cpu 在当前 CPU 上的返回值。
下面我们来看看函数
nr_iowait_cpu 的代码,该函数很简单,其实就是把指定 CPU 的
运行队列(Run Queue,
struct rq,
用于管理该 CPU 上所有可运行的进程
)的 nr_iowait 成员返回,我们可以猜测 nr_iowait 表示该 CPU 上
有几个正在因为等待 I/O 而被阻塞的进程
。
为了验证这个猜想,我们内核代码中搜索
nr_iowait,看看这个字段在哪儿被更新的。可以发现
nr_iowait 是在函数
__block_task 中被更新的。函数
__block_task 是由函数 __schedule 一路调下来的。熟悉 Linux 内核进程调度的同学都知道函数
__schedule 是 Linux 内核调度器的最核心函数,它负责执行真正的任务切换。当进程
执行了导致睡眠/阻塞的操作,比如等待 I/O 完成,最终都会调用到函数
__schedule
主动让出 CPU,这个时间点也是更新当前 CPU 运行队列的 nr_iowait 成员的最佳时间点,从
__block_task 的代码可以看出,如果当前进程的 in_iowait 非 0,则把当前 CPU
运行队列的 nr_iowait 值加 1,那么在函数
tick_nohz_stop_idle 中调用
nr_iowait_cpu 的返回值就是大于 0 的,则空闲时间就会被计入
iowait_sleeptime 了。所以这里更新 CPU 运行队列的
nr_iowait 的关键是该 CPU 上有进程的
in_iowait 成员非 0,并且该进程还要处于睡眠/阻塞状态。
下面我们继续搜索进程(struct
task_struct
)的
in_iowait 字段是在哪儿更新的,可以发现如下代码。在
io_schedule 和
io_schedule_timeout 这些函数中都会调用
io_schedule_prepare 和
io_schedule_finish 这两个函数。函数
io_schedule_prepare 会在当前进程(通过调用
schedule 或者
schedule_timeout,最终都会调到上面那个
__schedule)让出 CPU 之前调用,
io_schedule_prepare 会先保存当前进程
in_iowait 字段,然后把
in_iowait 设置为 1,表示当前进程处于等待 I/O 的状态。当进程被再次唤醒时,schedule 或者
schedule_timeout 函数就会返回,函数
io_schedule_finish 会被调用,它会把之前保存的
in_iowait 字段的旧值给恢复回去,通常情况都会把
in_iowait 字段给恢复为 0,因为进程这时已经被唤醒,它所等待的 I/O 极有可能已经被完成了,所以要让进程退出等待 I/O 的状态
。
通过上面的分析可以看出要想让进程等待 I/O 的时间被算进 iowait 中,进程等待 I/O 调用的函数需要是 io_schedule 和
io_schedule_timeout 这两个函数,因为这两个函数会把该进程的
in_iowait 字段置 1,把该进程标记为等待 I/O 的状态。
上面的分析是倒着从 /proc/stat 的实现一步一步分析出来 iowait 的值是怎么来的,下面我们正向叙述一遍。首先是进程在需要等待 I/O 的时候,要调用(主要是内核在关键路径上调用的)函数 io_schedule 或者
io_schedule_timeout 把 CPU 让出去,让自己处于阻塞状态,这两个函数会先把当前进程的
in_iowait 字段置 1,然后层层调用到
__schedule 函数让出 CPU,在让出 CPU 之前函数
__schedule 还要检查当前进程的
in_iowait 字段,如果非 0,则将当前 CPU 运行队列(struct rq)的
nr_iowait 字段加 1。如果当前 CPU 上目前也没有其他要运行的进程,该 CPU 会进入空闲状态,同时把进入空闲状态的时间点记录在该 CPU 的
tick_cpu_sched 的
idle_entrytime 成员中
。当后面某一刻 CPU 要退出空闲状态时,或者发生中断时,函数
tick_nohz_stop_idle 会被调用,该函数会根据当前 CPU 运行队列的
nr_iowait 是否大于 0,来决定这段空闲时间被计入哪个字段(
iowait_sleeptime 还是 idle
_sleeptime),如果
nr_iowait 大于 0,则说明在 CPU 这段空闲时间内,有进程在等待 I/O,则这段空闲时间会被累加到
iowait_sleeptime 上,否则这段空闲时间只能被累加到 idle
_sleeptime 上。最后当用户在读取伪文件 /proc/stat 的时候,内核会把每个 CPU 上记录的
iowait_sleeptime 和其他统计时间一起输出。
vmstat,iostat 和 mpstat 这些工具会每隔一段时间读取一次
伪文件 /proc/stat 的内容,然后算出前后两次读取到的 iowait 的差值,以及这个差值在这段时间占比,这就形成了这些工具实时监控的效果。通过
iowait 的来历我们也能看出
为什么这些系统监控工具能几乎无开销地获取精确的 CPU 统计信息 —— 数据已经在内核中维护好了,读取时只是简单的汇总和格式化。
本文来自网友投稿或网络内容,如有侵犯您的权益请联系我们删除,联系邮箱:596563534@qq.com 。
AI编程-记忆迷思
谈C编程---注释与代码混排的缺点
最新文章
AI编程-记忆迷思
生物传感器的可编程基因平台,刚登Science Advances!
Linux每日命令-08-cat
编程语言大PK!
GESP四级C++函数:从重复代码到模块化编程的蜕变
歪脖(vibe)编程一些趣事
PHP SWOOLE HTTP服务端
程序软件开发交流、编程问题高效答疑
拒绝“自嗨式”开发!为什么你的代码很牛,用户却觉得很难用?
开业庆典┃灵犀智创青少年编程中心盛大启航!
热门文章
玛酷机器人 | 2026编程寒假班/科创营招生啦!
AI编程实战分享!重庆理工大学站,我来了
Notion 的代码块与第三方工具的结合使用
家人问我:你不是用AI写代码吗,怎么还下班这么晚?我用亲身经历告诉你,AI革命到底给普通程序员带来了什么
AI 编程研讨会总结和问卷报告:OpenCode 正在超越 Cursor
Python数据分析:Python编程基础
学员家长小马智行技术总监分享对编程与数学教育的思考
当研发团队告别熬夜赶工和进度黑洞:用低代码自己造一个管理利器
为什么“可笑”的五行代码,能震撼硅谷?
能打游戏的行吗|学编程电脑配置要多高?
随机文章
10个月宝宝每天需要喝多少奶粉?
编程:城阳区公文格式处理|卫东老师一键排版工具|从代码到可执行文件的完整指南
Python实现本地文件管理器2.0
Python趣味编程:从入门到人工智能pdf电子书下载
当 AI 编程越来越容易,一种“人人可做”的新学习方式正在出现
初学python练题:使用if-elif-else结构输出每个数字对应的序数.
Linux 命令速查备忘手册来啦,建议收藏
Python版_Leetcode_hot100系列(16)--多维动态规划
Python 安装教程+永久激活版中文安装包(附全部版本安装包)
Tushare+Python 30行代码实现获取股票数据并存储到本地 SQLite
【Python代码】巨潮资讯网定向增发公告批量获取