IOwait 到底在wait什么

%IOwait 一个迷之参数,top/iostat/mpstat/sar 都会统计的关键数据,字面理解是系统等待IO的时间,经常用于反映系统IO压力。 IOwait时候CPU在干什么?什么时间才算IOwait,到底在wait什么?

 

数据含义及来源

man mpstat 查看下官方定义

%iowait
           Percentage  of  time that the CPU or CPUs were idle during which the system
           had an outstanding disk I/O request.

统计工具对IOwait的定义是CPU idle时系统有IO请求的时间百分比,也就是说要满足两个条件才会被记录为IOwait

1.CPU idle

2.有IO请求在处理

%iowait 数据来自/proc/stat 第5个参数,这里的数据又是怎么来的? 

 

内核IOwait时间统计(kernel 5.3)

1. /proc/stat 数据来源

fs/proc/stat.c show_stat() 中找到/proc/stat 获取 iowait的路径 get_iowait_time()->get_cpu_iowait_time_us()。

根据CPU当前状态 online/offline,选择从cpustat.iowait 或者get_cpu_iowait_time_us() 获取iowait

static u64 get_iowait_time(struct kernel_cpustat *kcs, int cpu)
{
	u64 iowait, iowait_usecs = -1ULL;

	if (cpu_online(cpu))
		iowait_usecs = get_cpu_iowait_time_us(cpu, NULL);

	if (iowait_usecs == -1ULL)
		/* !NO_HZ or cpu offline so we can rely on cpustat.iowait */
		iowait = kcs->cpustat[CPUTIME_IOWAIT];
	else
		iowait = iowait_usecs * NSEC_PER_USEC;

	return iowait;
}

get_cpu_iowait_time_us()数据来源于每个CPU 的 ts->iowait_sleeptime

u64 get_cpu_iowait_time_us(int cpu, u64 *last_update_time)
{
	struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu);
	ktime_t now, iowait;

	if (!tick_nohz_active)
		return -1;

	now = ktime_get();
	if (last_update_time) {
		update_ts_time_stats(cpu, ts, now, last_update_time);
		iowait = ts->iowait_sleeptime;
	} else {
		if (ts->idle_active && nr_iowait_cpu(cpu) > 0) {
			ktime_t delta = ktime_sub(now, ts->idle_entrytime);

			iowait = ktime_add(ts->iowait_sleeptime, delta);
		} else {
			iowait = ts->iowait_sleeptime;
		}
	}
	return ktime_to_us(iowait);
}

即iowait 来源为 cpustat.iowait  或者 CPU 相关的 ts->iowait_sleeptime 。

 

2. cpustat.iowait/iowait_sleeptime 统计

这两个参数的累加函数为

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;
}
static void
update_ts_time_stats(int cpu, struct tick_sched *ts, ktime_t now, u64 *last_update_time)
{
	ktime_t delta;

	if (ts->idle_active) {
		delta = ktime_sub(now, ts->idle_entrytime);
		if (nr_iowait_cpu(cpu) > 0)
			ts->iowait_sleeptime = ktime_add(ts->iowait_sleeptime, delta);
		else
			ts->idle_sleeptime = ktime_add(ts->idle_sleeptime, delta);
		ts->idle_entrytime = now;
	}

	if (last_update_time)
		*last_update_time = ktime_to_us(now);
}

调用栈为:

do_idle()->tick_nohz_idle_exit()->__tick_nohz_idle_restart_tick()->tick_nohz_account_idle_ticks()->account_idle_ticks()->account_idle_time()

do_idle()->tick_nohz_idle_exit()->tick_nohz_stop_idle()->update_ts_time_stats()

即CPU idle时会触发计数,具体计数哪个根据cpu状态判断。

两个数据的计数逻辑一样,都是根据当前cpu runqueue的nr_iowait 判断时间要累加在idle 或者 iowait 。

 

3.nr_iowait 哪里统计

调度时若当前task是in_iowait,则当前CPU runqueue 的nr_iowait 加1,表示当前有task在等待IO,参考__schedule() 。

 

	if (!preempt && prev->state) {
		if (signal_pending_state(prev->state, prev)) {
			prev->state = TASK_RUNNING;
		} else {
			deactivate_task(rq, prev, DEQUEUE_SLEEP | DEQUEUE_NOCLOCK);

			if (prev->in_iowait) {
				atomic_inc(&rq->nr_iowait);
				delayacct_blkio_start();
			}
		}
		switch_count = &prev->nvcsw;
	}

task的in_iowait 在 io_schedule_prepare()中设置,调用io_schedule_prepare()的相关函数有io_schedule(), io_schedule_time() , mutex_lock_io() , mutex_lock_io_nested() 等。 即当因这些调用产生调度则标记当前CPU有iowait的task,task重新wakeup时in_iowait恢复,cpu runqueu 的nr_iowait减1。

int io_schedule_prepare(void)
{
	int old_iowait = current->in_iowait;

	current->in_iowait = 1;
	blk_schedule_flush_plug(current);

	return old_iowait;
}

总的来说当有task因为IO而被调度出CPU时,标识该CPU有在等待IO的task,当CPU进入idle时如果仍有等待IO的task,就标记这段时间为IOwait,否则标记为idle, 与man mpstat中的定义一致。

 

IO在哪里阻塞

系统中执行IO的流程非常的多,其阻塞点也很多,这里列出两个通常IO操作中最常见的阻塞点。

1. io提交至驱动后,等待数据返回。

2. 有并发IO请求时争抢IO软/硬队列等资源。

用 kprobe 观察两种测试场景,单线程IO/多线程IO,的io_schedule调用栈

单线程read,仅有一种调用栈

             fio-7834  [001] d... 875382.127151: io_schedule: (io_schedule+0x0/0x40)
             fio-7834  [001] d... 875382.127163: <stack trace>
 => io_schedule
 => ext4_file_read_iter
 => new_sync_read
 => __vfs_read
 => vfs_read
 => ksys_pread64
 => sys_pread64
 => ret_fast_syscall

多线程read,多了因争抢IO资源产生的io调度

            fio-9800  [001] d... 875471.769845: io_schedule: (io_schedule+0x0/0x40)
             fio-9800  [001] d... 875471.769858: <stack trace>
 => io_schedule
 => ext4_file_read_iter
 => new_sync_read
 => __vfs_read
 => vfs_read
 => ksys_pread64
 => sys_pread64
 => ret_fast_syscall
 => 0xbe9445a8
             fio-9801  [003] d... 875471.770153: io_schedule: (io_schedule+0x0/0x40)
             fio-9801  [003] d... 875471.770164: <stack trace>
 => io_schedule
 => blk_mq_get_request
 => blk_mq_make_request
 => generic_make_request
 => submit_bio
 => ext4_mpage_readpages
 => ext4_readpages
 => read_pages
 => __do_page_cache_readahead
 => force_page_cache_readahead
 => page_cache_sync_readahead
 => generic_file_read_iter
 => ext4_file_read_iter
 => new_sync_read
 => __vfs_read
 => vfs_read
 => ksys_pread64
 => sys_pread64
 => ret_fast_syscall
 => 0xbe9445a8

 

结论

IOwait 是指CPU空闲时,且当前有task在等待IO的时间。

因IO阻塞而调度主要出现在 1.等待数据返回; 2.并发IO时竞争资源

影响该时间的因素很多,不只有IO负载,CPU负载也会严重影响该参数,不可一味根据IOwait判断系统的IO压力,还需要结合iostat 等数据综合判断。

 

 

<think>嗯,用户问的是关于“io wait 0”,首先我得确认这个术语的具体含义。通常来说,io wait指的是系统在等待I/O操作完成时所消耗的CPU时间百分比,也就是iowait。当这个值为0时,可能意味着系统当前没有因为I/O操作而等待,或者有其他情况。 首先,我得回忆一下iowait的定义。iowait是CPU空闲并且有未完成的I/O请求的时间占总CPU时间的百分比。所以,如果iowait为0,可能有几种情况:要么系统没有I/O请求,所有I/O操作都及时完成,没有让CPU等待;或者系统负载很低,I/O操作非常少;或者可能是监控工具的问题,比如采样时间太短,没有捕捉到iowait的情况。 接下来,我需要考虑不同的场景。比如,在服务器高负载的情况下,iowait通常不会为0,特别是如果有大量的磁盘读写操作。但如果用户是在一个轻负载的系统上测试,或者使用的是SSD,I/O响应速度很快,iowait可能接近0。这时候需要结合其他指标,如磁盘使用率(util)、读写速度(r/s, w/s)来综合判断。 另外,用户可能是在查看某些监控工具的输出,比如top、vmstat或者iostat。不同的工具可能在计算iowait时有不同的方式,需要确认用户使用的工具和命令参数是否正确。例如,iostat的某些版本可能需要指定采样间隔,否则默认显示的是自启动以来的平均值,可能不准确。 还需要考虑系统配置,比如是否使用了异步I/O或者缓存机制,这些都可能减少iowait的时间。例如,如果应用程序使用了足够的缓存,大部分I/O操作可能被缓冲,从而减少实际的磁盘访问,降低iowait。 另外,用户可能误解了iowait为0的含义,认为系统没有I/O活动,但实际上可能I/O处理非常高效,或者CPU正在处理其他任务,没有处于空闲状态。因为iowait只有在CPU空闲时才会计算,如果CPU一直忙于处理其他任务,即使有I/O请求,iowait也可能显示为0。 最后,应该建议用户如何进一步诊断。比如使用iostat -x 1来实时查看磁盘I/O状况,检查await、util等指标,或者使用pidstat来查找具体的进程I/O情况。同时,提醒用户结合上下文和其他性能指标综合分析,避免单一指标导致的误判。</think>### 关于 **I/O Wait 0** 的解释 **I/O Wait**(输入/输出等待时间)是衡量系统性能的重要指标,表示 CPU 在等待 I/O 操作(如磁盘读写、网络传输)完成时的空闲时间占比。当 `I/O Wait` 值为 **0** 时,可能表示以下情况: --- #### 1. **无 I/O 阻塞** - **场景**:系统当前没有因 I/O 操作而阻塞的任务。 - **原因**: - 应用程序未进行密集的磁盘或网络操作。 - I/O 请求被快速处理(如使用 SSD 或高速缓存)。 - **示例**:轻量级 Web 服务器空闲时,`iowait` 可能接近 0。 --- #### 2. **CPU 繁忙** - **场景**:CPU 正在处理计算密集型任务,无暇进入空闲状态。 - **关键点**:`iowait` 仅在 CPU **空闲且等待 I/O** 时统计。如果 CPU 始终忙碌,即使有 I/O 请求,`iowait` 也会显示为 0。 - **示例**:运行 `stress --cpu 4` 时,CPU 使用率 100%,`iowait` 可能为 0。 --- #### 3. **监控工具限制** - **问题**:某些工具(如 `top`)的采样间隔较短,可能无法捕捉瞬时 I/O 活动。 - **解决方法**:使用 `iostat -x 1` 查看更精确的实时 I/O 统计。 --- #### 4. **系统优化结果** - **场景**:通过异步 I/O、缓存(如 `PageCache`)或预读技术减少阻塞。 - **示例**:数据库启用写缓存后,`iowait` 可能显著降低。 --- ### 如何验证 I/O 状态? 1. **检查磁盘利用率**: ```bash iostat -x 1 ``` - 关注 `%util`(磁盘利用率):若接近 100%,说明磁盘繁忙,但 `iowait` 为 0 可能是 CPU 未空闲。 2. **查看进程级 I/O**: ```bash pidstat -d 1 ``` - 定位具体进程的 I/O 读写速率(`kB_rd/s`、`kB_wr/s`)。 3. **综合分析工具**: - 使用 `vmstat 1` 观察 `wa`(iowait 列)和 `bi`/`bo`(块输入/输出)。 --- ### 总结表 | 情况 | 表现 | 建议操作 | |---------------------|-------------------------------|------------------------| | 无 I/O 需求 | `iowait=0`,磁盘利用率低 | 无需处理 | | CPU 繁忙 | `%CPU` 接近 100%,`iowait=0` | 优化 CPU 密集型任务 | | 瞬时采样误差 | 工具显示 `iowait` 波动 | 延长监控时间间隔 | | 高效 I/O 系统 | `iowait=0` 但磁盘吞吐量高 | 确认是否满足业务需求 | 若长期 `iowait=0` 但系统响应缓慢,需检查是否因 CPU 过载导致 I/O 请求排队。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值