从上述结果中可以看出,我的机器的每秒要打出 1000 次节拍 。也就是每 1 ms 一次 。
每次当时间中断到来的时候,都会调用 update_process_times 来更新系统时间 。更新后的时间都存储在我们前面提到的 percpu 变量 kcpustat_cpu 中 。
文章插图
我们来详细看下汇总过程 update_process_times 的源码,它位于 kernel/time/timer.c 文件中 。
//file:kernel/time/timer.cvoid update_process_times(int user_tick){ struct task_struct *p = current; //进行时间累积处理 account_process_tick(p, user_tick); ...}
这个函数的参数 user_tick 值得是采样的瞬间是处于内核态还是用户态 。接下来调用 account_process_tick 。//file:kernel/sched/cputime.cvoid account_process_tick(struct task_struct *p, int user_tick){ cputime = TICK_NSEC; ... if (user_tick)//3.1 统计用户态时间account_user_time(p, cputime); else if ((p != rq->idle) || (irq_count() != HARDIRQ_OFFSET))//3.2 统计内核态时间account_system_time(p, HARDIRQ_OFFSET, cputime); else//3.3 统计空闲时间account_idle_time(cputime);}
在这个函数中,首先设置 cputime = TICK_NSEC, 一个 TICK_NSEC 的定义是一个节拍所占的纳秒数 。接下来根据判断结果分别执行 account_user_time、account_system_time 和 account_idle_time 来统计用户态、内核态和空闲时间 。3.1 用户态时间统计
//file:kernel/sched/cputime.cvoid account_user_time(struct task_struct *p, u64 cputime){ //分两种种情况统计用户态 CPU 的使用情况 int index; index = (task_nice(p) > 0) ? CPUTIME_NICE : CPUTIME_USER; //将时间累积到 /proc/stat 中 task_group_account_field(p, index, cputime); ......}
account_user_time 函数主要分两种情况统计:- 如果进程的 nice 值大于 0,那么将会增加到 CPU 统计结构的 nice 字段中 。
- 如果进程的 nice 值小于等于 0,那么增加到 CPU 统计结构的 user 字段中 。
我们平时如果想要观察系统的用户态消耗的时间的话,应该是将 top 中输出的 user 和 nice 加起来一并考虑,而不是只看 user!
接着调用 task_group_account_field 来把时间加到前面我们用到的 kernel_cpustat 内核变量中 。
//file:kernel/sched/cputime.cstatic inline void task_group_account_field(struct task_struct *p, int index,u64 tmp){ __this_cpu_add(kernel_cpustat.cpustat[index], tmp); ...}
3.2 内核态时间统计我们再来看内核态时间是如何统计的,找到 account_system_time 的代码 。//file:kernel/sched/cputime.cvoid account_system_time(struct task_struct *p, int hardirq_offset, u64 cputime){ if (hardirq_count() - hardirq_offset)index = CPUTIME_IRQ; else if (in_serving_softirq())index = CPUTIME_SOFTIRQ; elseindex = CPUTIME_SYSTEM; account_system_index_time(p, cputime, index);}
内核态的时间主要分 3 种情况进行统计 。- 如果当前处于硬中断执行上下文, 那么统计到 irq 字段中
- 如果当前处于软中断执行上下文, 那么统计到 softirq 字段中
- 否则统计到 system 字段中
//file:kernel/sched/cputime.cstatic inline void task_group_account_field(struct task_struct *p, int index,u64 tmp){__this_cpu_add(kernel_cpustat.cpustat[index], tmp);}
3.3 空闲时间的累积没错,在内核变量 kernel_cpustat 中不仅仅是统计了各种用户态、内核态的使用统计,空闲也一并统计起来了 。如果在采样的瞬间,cpu 既不在内核态也不在用户态的话,就将当前节拍的时间都累加到 idle 中 。
//file:kernel/sched/cputime.cvoid 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; elsecpustat[CPUTIME_IDLE] += cputime;}
在 cpu 空闲的情况下,进一步判断当前是不是在等待 IO(例如磁盘 IO),如果是的话这段空闲时间会加到 iowait 中,否则就加到 idle 中 。从这里,我们可以看到 iowait 其实是 cpu 的空闲时间,只不过是在等待 IO 完成而已 。看到这里,开篇问题 3 也有非常明确的答案了,io wait 其实是 cpu 在空闲状态的一项统计,只不过这种状态和 idle 的区别是 cpu 是因为等待 io 而空闲 。
推荐阅读
- Go 语言切片是如何扩容的?
- HTTPS是如何保证密文不能被篡改的?
- win10系统如何显示文件扩展名
- |万能钓饵的正确用法,渔获差异巨大,原因是少了几个步骤
- 袁大头|民国时期袁大头贰角目前市场价值如何
- 魏璎珞|《延禧攻略》是古代职场升职记?魏璎珞教你如何在后宫做人
- 钱币|明代时期大中通宝折五钱收藏空间如何
- |凌天二代黑坑竿实钓检测,表现如何?能拿坑冠么?
- mbti|探索MBTI-79:判断型(J)如何在职场中做好压力管理
- |职场情商:办公室同事之间的矛盾不要激化,如何化解才是职场王道