【内核调度、负载计算】【update_curr】
负载计算,这里指的是基于pelt的负载计算,在很多时候都会用到,基于之前的负载均衡的地方,在dequeue_entity和enqueue_entity都调用了update_curr函数进行负载的更新
update_curr
Update the current task's runtime statistics.
/** Update the current task's runtime statistics.*/
static void update_curr(struct cfs_rq *cfs_rq)
{struct sched_entity *curr = cfs_rq->curr;u64 now = rq_clock_task(rq_of(cfs_rq));u64 delta_exec;if (unlikely(!curr))return;/* (3.2.1.1) 计算cfs_rq->curr se的实际执行时间 */ delta_exec = now - curr->exec_start;if (unlikely((s64)delta_exec <= 0))return;curr->exec_start = now;schedstat_set(curr->statistics.exec_max, max(delta_exec, curr->statistics.exec_max));curr->sum_exec_runtime += delta_exec;// (1) 累计当前进程的实际运行时间
// 更新cfs_rq的实际执行时间cfs_rq->exec_clockschedstat_add(cfs_rq, exec_clock, delta_exec);
/* (3.2.1.2) 计算cfs_rq->curr se的虚拟执行时间vruntime */curr->vruntime += calc_delta_fair(delta_exec, curr);// (2) 累计当前进程的vruntimeupdate_min_vruntime(cfs_rq);
/* (3.2.1.3) 如果se对应的是task,而不是task_group,更新task对应的时间统计*/if (entity_is_task(curr)) {struct task_struct *curtask = task_of(curr);trace_sched_stat_runtime(curtask, delta_exec, curr->vruntime);// 更新task所在cgroup之cpuacct的某个cpu运行时间ca->cpuusage[cpu]->cpuusagecpuacct_charge(curtask, delta_exec);// 统计task所在线程组(thread group)的运行时间:// tsk->signal->cputimer.cputime_atomic.sum_exec_runtimeaccount_group_exec_runtime(curtask, delta_exec);}
/* (3.2.1.4) 计算cfs_rq的运行时间,是否超过cfs_bandwidth的限制:cfs_rq->runtime_remaining*/account_cfs_rq_runtime(cfs_rq, delta_exec);
}
calc_delta_fair
如果说unlikely是极其不太可能发生,那么这里我们可以假设vruntime在nice_0的条件下就等于runtime
通过注释可以知道vruntime = runtime* NICE_0_LOAD / weight
/*
* delta /= w
*/
static inline u64 calc_delta_fair(u64 delta, struct sched_entity *se)
{
if (unlikely(se->load.weight != NICE_0_LOAD))
delta = __calc_delta(delta, NICE_0_LOAD, &se->load);
return delta;
}
/** delta_exec * weight / lw.weight* OR* (delta_exec * (weight * lw->inv_weight)) >> WMULT_SHIFT** Either weight := NICE_0_LOAD and lw \e sched_prio_to_wmult[], in which case* we're guaranteed shift stays positive because inv_weight is guaranteed to* fit 32 bits, and NICE_0_LOAD gives another 10 bits; therefore shift >= 22.** Or, weight =< lw.weight (because lw.weight is the runqueue weight), thus* weight/lw.weight <= 1, and therefore our shift will also be positive.*/
static u64 __calc_delta(u64 delta_exec, unsigned long weight, struct load_weight *lw)
{u64 fact = scale_load_down(weight);int shift = WMULT_SHIFT;__update_inv_weight(lw);if (unlikely(fact >> 32)) {while (fact >> 32) {fact >>= 1;shift--;}}/* hint to use a 32x32->64 mul */fact = (u64)(u32)fact * lw->inv_weight;while (fact >> 32) {fact >>= 1;shift--;}return mul_u64_u32_shr(delta_exec, fact, shift);
}static void __update_inv_weight(struct load_weight *lw)
{unsigned long w;if (likely(lw->inv_weight))return;w = scale_load_down(lw->weight);if (BITS_PER_LONG > 32 && unlikely(w >= WMULT_CONST))lw->inv_weight = 1;else if (unlikely(!w))lw->inv_weight = WMULT_CONST;elselw->inv_weight = WMULT_CONST / w;
}
update_min_vruntime(cfs_rq)
min_vruntime的大小和哪些条件有关系呢?
static void update_min_vruntime(struct cfs_rq *cfs_rq)
{/* 初始化vruntime的值, 相当于如下的代码if (cfs_rq->curr != NULL)vruntime = cfs_rq->curr->vruntime;elsevruntime = cfs_rq->min_vruntime;*/// 这里两次赋值实现了一个if else的操作u64 vruntime = cfs_rq->min_vruntime;if (cfs_rq->curr)vruntime = cfs_rq->curr->vruntime;/* 检测红黑树是都有最左的节点, 即是否有进程在树上等待调度* cfs_rq->rb_leftmost(struct rb_node *)存储了进程红黑树的最左节点* 这个节点存储了即将要被调度的结点 * */if (cfs_rq->rb_leftmost) {struct sched_entity *se = rb_entry(cfs_rq->rb_leftmost,struct sched_entity,run_node);
/* 如果就绪队列上没有curr进程* 则vruntime设置为树种最左结点的vruntime* 否则设置vruntiem值为cfs_rq->curr->vruntime和se->vruntime的最小值*/if (!cfs_rq->curr)vruntime = se->vruntime;elsevruntime = min_vruntime(vruntime, se->vruntime);}/* ensure we never gain time by being placed backwards. * 为了保证min_vruntime单调不减* 只有在vruntime超出的cfs_rq->min_vruntime的时候才更新*/cfs_rq->min_vruntime = max_vruntime(cfs_rq->min_vruntime, vruntime);
#ifndef CONFIG_64BITsmp_wmb();cfs_rq->min_vruntime_copy = cfs_rq->min_vruntime;
#endif
}
附加:更新最小vruntime时间,以便进程最先被选择
活动进程curr | 待调度进程rb_leftmost | 可能的vruntime值 | cfs_rq |
---|---|---|---|
NULL | NULL | cfs_rq->min_vruntime | 维持原值 |
NULL | 非NULL | rb_leftmost->se->vruntime | max(可能值vruntime, 原值) |
非NULL | NULL | curr->vruntime | max(可能值vruntime, 原值) |
非NULL | 非NULL | min(curr->vruntime, rb_leftmost->se->vruntime) | max(可能值vruntime, 原值) |
在另外一个版本中,源码有一点点的变化
static void update_min_vruntime(struct cfs_rq *cfs_rq)
{struct sched_entity *curr = cfs_rq->curr;struct rb_node *leftmost = rb_first_cached(&cfs_rq->tasks_timeline);u64 vruntime = cfs_rq->min_vruntime;if (curr) {if (curr->on_rq)vruntime = curr->vruntime;elsecurr = NULL;}if (leftmost) { /* non-empty tree */struct sched_entity *se;se = rb_entry(leftmost, struct sched_entity, run_node);if (!curr)vruntime = se->vruntime;elsevruntime = min_vruntime(vruntime, se->vruntime);}/* ensure we never gain time by being placed backwards. */cfs_rq->min_vruntime = max_vruntime(cfs_rq->min_vruntime, vruntime);
#ifndef CONFIG_64BITsmp_wmb();cfs_rq->min_vruntime_copy = cfs_rq->min_vruntime;
#endif
}
min_vruntime实际上当task在不同的cpu之间做迁移的时候,先减去本cpu的min_vruntime,然后在加上dst_cpu的min_vruntime
【内核调度、负载计算】【update_curr】相关推荐
- linux内核cpu负载计算,CPU 负载 — The Linux Kernel documentation
CPU 负载¶ Linux通过``/proc/stat``和``/proc/uptime``导出各种信息,用户空间工具 如top(1)使用这些信息计算系统花费在某个特定状态的平均时间. 例如: $ i ...
- linux内核cpu负载计算,Load和CPU利用率是如何算出来的
本文内容遵从CC版权协议, 可以随意转载, 但必须以超链接形式标明文章原始出处和作者信息及版权声明网址: http://www.penglixun.com/tech/system/how_to_cal ...
- CFS调度器负载计算
/******以下结论和代码分析都是基于最新Linux master分支(Linux5.0)******/ 1. 负载结构体 每个调度实体都有一个负载结构,用来跟踪调度实体对系统的负载贡献 struc ...
- 【Linux 内核】CFS 调度器 ③ ( 计算进程 “ 虚拟运行时间 “ )
文章目录 一.计算进程 " 虚拟运行时间 " 一.计算进程 " 虚拟运行时间 " 在上一篇博客 [Linux 内核]CFS 调度器 ② ( CFS 调度器 &q ...
- 【Linux 内核】CFS 调度器 ② ( CFS 调度器 “ 权重 “ 概念 | CFS 调度器调度实例 | 计算进程 “ 实际运行时间 “ )
文章目录 一.CFS 调度器 " 权重 " 概念 二.CFS 调度器调度实例 ( 计算进程 " 实际运行时间 " ) 一.CFS 调度器 " 权重 & ...
- linux内核死锁检测机制 | oenhan,Linux内核CPU负载均衡机制 | OenHan
还是神奇的进程调度问题引发的,参看Linux进程组调度机制分析,组调度机制是看清楚了,发现在重启过程中,很多内核调用栈阻塞在了double_rq_lock函数上,而double_rq_lock则是lo ...
- linux 2.6内核进程调度,Linux2.4与Linux2.6内核调度器的比较研究
Linux的内核开发是一个漫长的过程,自2001年11月开发出2.5.0以来,Linux内核的发展十分迅速,作了很多重大的改进,性能也有了很大的提高.内核调度器的改进是最主要的进步之一,本文对比研究了 ...
- Go 如何利用 Linux 内核的负载均衡能力?
在测试 HTTP 服务时,如果该进程我们忘记关闭,而重新尝试启动一个新的服务进程,那么将会遇到类似以下的错误信息: $ go run main.go listen tcp :8000: bind: a ...
- Go 如何利用 Linux 内核的负载均衡能力
在测试 HTTP 服务时,如果该进程我们忘记关闭,而重新尝试启动一个新的服务进程,那么将会遇到类似以下的错误信息: $ go run main.go listen tcp :8000: bind: a ...
最新文章
- C#进阶系列——动态Lamada
- DataList中的按钮触发事件的方法的实现
- SuperMap iDesktop 8C 进行地图SQL查询并显示结果操作示例
- MATLAB应用实战系列( 七十五) -图像处理应用 MATLAB实现基于分水岭算法的图像分割 (附matlab代码)
- mysql 修改字段为1-10的随机数
- 数据中心(机房)施工方案
- Ubuntu 16.04 QT ‘usr/bin/ld cannot find -IGL‘
- 大数据WEB阶段Spring框架(二)简化配置的操作
- DHCP : 网络世界身份的获取
- 依赖注入的几种形式及场景
- python paramiko模块下载_Python自动化运维实战:使用Python管理网络设备
- 闪屏页面(Splash)开发
- hihoCoder 1080 : 更为复杂的买卖房屋姿势 线段树区间更新
- 云桌面服务器+搭建,搭建自己的云桌面服务器
- Qt调试模式提示 Temporarily disabling breakpoints for unloaded shared library
- 零基础带你玩转微信小程序--小程序的基础和安装
- 计算机三维设计论文摘要,三维动画论文摘要
- [转载]Malcolm的新书:Outliers
- 德州仪器TI芯片自动下单抢购监控软件技术分析
- 助力苏州工业园区从“平民公交”转向“全民公交” ⑤
热门文章
- 新书上市 | 《Java性能优化实践》,众多业内大佬推荐阅读
- 学习Easeljs 笔记
- 爱立信在中国市场连续受挫,但它在美国市场斩获538亿元大单
- linux(debian11)系统安装那些事儿--没有无线网需要安装无线网卡驱动双显卡等问题
- 一个网页显示歌词的音乐播放器
- 阿里产品岗需是技术出身?分享技术转型产品的成功经验
- android手机录屏多少fps,如何在Android上以90fps或120fps的屏幕录制?
- 网络安全-日志监控-关联分析-大数据
- 计算机软件创业论文,计算机软件专业创新创业人才培养对策论文
- HTML学习笔记-基础知识整理