负载计算,这里指的是基于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】相关推荐

  1. linux内核cpu负载计算,CPU 负载 — The Linux Kernel documentation

    CPU 负载¶ Linux通过``/proc/stat``和``/proc/uptime``导出各种信息,用户空间工具 如top(1)使用这些信息计算系统花费在某个特定状态的平均时间. 例如: $ i ...

  2. linux内核cpu负载计算,Load和CPU利用率是如何算出来的

    本文内容遵从CC版权协议, 可以随意转载, 但必须以超链接形式标明文章原始出处和作者信息及版权声明网址: http://www.penglixun.com/tech/system/how_to_cal ...

  3. CFS调度器负载计算

    /******以下结论和代码分析都是基于最新Linux master分支(Linux5.0)******/ 1. 负载结构体 每个调度实体都有一个负载结构,用来跟踪调度实体对系统的负载贡献 struc ...

  4. 【Linux 内核】CFS 调度器 ③ ( 计算进程 “ 虚拟运行时间 “ )

    文章目录 一.计算进程 " 虚拟运行时间 " 一.计算进程 " 虚拟运行时间 " 在上一篇博客 [Linux 内核]CFS 调度器 ② ( CFS 调度器 &q ...

  5. 【Linux 内核】CFS 调度器 ② ( CFS 调度器 “ 权重 “ 概念 | CFS 调度器调度实例 | 计算进程 “ 实际运行时间 “ )

    文章目录 一.CFS 调度器 " 权重 " 概念 二.CFS 调度器调度实例 ( 计算进程 " 实际运行时间 " ) 一.CFS 调度器 " 权重 & ...

  6. linux内核死锁检测机制 | oenhan,Linux内核CPU负载均衡机制 | OenHan

    还是神奇的进程调度问题引发的,参看Linux进程组调度机制分析,组调度机制是看清楚了,发现在重启过程中,很多内核调用栈阻塞在了double_rq_lock函数上,而double_rq_lock则是lo ...

  7. linux 2.6内核进程调度,Linux2.4与Linux2.6内核调度器的比较研究

    Linux的内核开发是一个漫长的过程,自2001年11月开发出2.5.0以来,Linux内核的发展十分迅速,作了很多重大的改进,性能也有了很大的提高.内核调度器的改进是最主要的进步之一,本文对比研究了 ...

  8. Go 如何利用 Linux 内核的负载均衡能力?

    在测试 HTTP 服务时,如果该进程我们忘记关闭,而重新尝试启动一个新的服务进程,那么将会遇到类似以下的错误信息: $ go run main.go listen tcp :8000: bind: a ...

  9. Go 如何利用 Linux 内核的负载均衡能力

    在测试 HTTP 服务时,如果该进程我们忘记关闭,而重新尝试启动一个新的服务进程,那么将会遇到类似以下的错误信息: $ go run main.go listen tcp :8000: bind: a ...

最新文章

  1. C#进阶系列——动态Lamada
  2. DataList中的按钮触发事件的方法的实现
  3. SuperMap iDesktop 8C 进行地图SQL查询并显示结果操作示例
  4. MATLAB应用实战系列( 七十五) -图像处理应用 MATLAB实现基于分水岭算法的图像分割 (附matlab代码)
  5. mysql 修改字段为1-10的随机数
  6. 数据中心(机房)施工方案
  7. Ubuntu 16.04 QT ‘usr/bin/ld cannot find -IGL‘
  8. 大数据WEB阶段Spring框架(二)简化配置的操作
  9. DHCP : 网络世界身份的获取
  10. 依赖注入的几种形式及场景
  11. python paramiko模块下载_Python自动化运维实战:使用Python管理网络设备
  12. 闪屏页面(Splash)开发
  13. hihoCoder 1080 : 更为复杂的买卖房屋姿势 线段树区间更新
  14. 云桌面服务器+搭建,搭建自己的云桌面服务器
  15. Qt调试模式提示 Temporarily disabling breakpoints for unloaded shared library
  16. 零基础带你玩转微信小程序--小程序的基础和安装
  17. 计算机三维设计论文摘要,三维动画论文摘要
  18. [转载]Malcolm的新书:Outliers
  19. 德州仪器TI芯片自动下单抢购监控软件技术分析
  20. 助力苏州工业园区从“平民公交”转向“全民公交” ⑤

热门文章

  1. 新书上市 | 《Java性能优化实践》,众多业内大佬推荐阅读
  2. 学习Easeljs 笔记
  3. 爱立信在中国市场连续受挫,但它在美国市场斩获538亿元大单
  4. linux(debian11)系统安装那些事儿--没有无线网需要安装无线网卡驱动双显卡等问题
  5. 一个网页显示歌词的音乐播放器
  6. 阿里产品岗需是技术出身?分享技术转型产品的成功经验
  7. android手机录屏多少fps,如何在Android上以90fps或120fps的屏幕录制?
  8. 网络安全-日志监控-关联分析-大数据
  9. 计算机软件创业论文,计算机软件专业创新创业人才培养对策论文
  10. HTML学习笔记-基础知识整理