Linux 内核- task_h_load

Linux Kernel- task_h_load

我想了解 load_balance 函数期间发生了什么。
我正在检查 3.14 版,但我也查看了 4.3 版,因为有人告诉我该机制已更改并且在此版本中更加清晰。

v3.14 中调用来自 move_tasks

v4.3 中调用来自 detach_tasks

据我所知,它是相同的功能,只是名称不同。

此函数根据 env->balance 参数将任务从一个队列移动到另一个队列。
我不明白的是 what/how 负载是在 task_h_load 中计算的。

有谁知道在task_h_load函数中load成员代表什么以及如何计算的?

如果 SMP configured.Linux 使用 Completely Fair Scheduler(CFS) to schedule each task so that each one obtain “fair” share of the processor time.. CFS uses concept of red-black tree,则会调用负载平衡。

调度器记录任务进入运行队列的当前时间。当进程等待处理器时间时,它的“等待”值会增加一个数量,该数量是从 运行 队列中当前的任务总数和进程优先级得出的。当处理器 运行 执行此任务时,此 "wait" 值会递减。如果此值低于某个值,则调度程序将抢占该任务,而其他任务更接近处理器来执行。 CFS 总是试图通过将 "wait" 值保持为零来保持理想状态。

linux load_balanceselect_task_rq_fair() 中有两个函数执行负载平衡任务。

简单来说,CFS 负载平衡机制将繁忙的 CPU 卸载到不太忙或理想的状态。

task_h_load用于计算任务的权重。

What I don't understand is what/how the load is calculated in task_h_load. how it is calculated in the task_h_load function?

这个权重因子取决于进程的 nice 值。

weighting factor = weight of process with nice value 0 / weight of current task;

其中‘weight’约等于 1024 * (1.25)^(-nice)

例如: 权重为 820,表示 nice value 1 权重为 1277,nice 值为 -1

task_h_load
有关更多 load-balance 方法和基础知识,请参阅 kernel comment task_h_load 使用 update_cfs_rq_h_load 计算 运行 队列的分层负载以进行公平时间调度,并使用 cfs_rq_load_avg return 运行 队列的平均负载 load_avg

虚拟 运行 时间是任务在 CPU 上的加权时间 运行。 CFS 总是试图保持 this Red-black 树的平衡。

<--较小的值------------v运行时间值--------较大的值-->

每个运行nable任务基于vruntime放置在自平衡的红黑树中。如果任务准备好运行(意味着任务没有等待任何资源),它被放置到树中。如果任务正在等待某些资源(即等待 I/O ),则将其删除。处理时间较少(意味着 vruntime 更小)的任务位于树的左侧,处理时间较长的任务位于树的右侧。

左节点的键值最小(对于CFS,是优先级较高的任务)。自平衡红黑树需要 O(lgN) 操作才能导航到左节点。调度程序将此值缓存在 rb_leftmost 中。通过仅检索此值,调度程序确定 运行 下一个

的任务

此负载平衡仅用于非实际任务 for real time 任务 push-pull 操作由 Steven Rostedt 和 Gregory Haskins

开发

关于 CFS 的另一件事也有助于公平组调度。考虑 below figure

move_task 只是尝试将不平衡加权负载(如上所示计算负载因子后的意思)从繁忙提高到 this_rq。它试图平衡两个 运行 队列的负载平衡,以便两者都可以节省 "fair" 处理器时间。

detach_task 为 linux 内核的 env 指针中指定的迁移分离任务。

detach_one_task 尝试从 env->src_rq 中取出一个任务。

detach_tasks() 尝试从中分离不平衡加权负载 busiest_rq。它 returns 分离任务的数量,否则如果分离失败则为零。

要将这个分离的任务附加到新的rq(运行队列),根据情况使用attach_task, attach_one_task,attach_tasks

detach_tasks 中引入了新的警告检查 lockdep_assert_held(),而 move_tasks

中没有

On multi-processor it is not easy to move task so easily 所以 cfs 做域特定 load-balancing 如下:

为了理解所有这些,我希望您阅读以下参考资料

  1. Per-Entity-Load-Tracking metric for load balancing

  2. weight factor

  3. Open-suse

  4. Robert-love Chapter 3

  5. CPU-scheduling

为了回答你的问题,我特地阅读了所有这些文档,如果有什么遗漏,希望你不介意发表评论。

CFS 持有 "scheduling entities" 的树。每个调度实体都可以有自己的树,以递归的方式等等...... (这很有用,例如,将特定用户的所有进程分组到一个调度实体中;从而防止任务较多的用户比进程较少的用户消耗更多 cpu 时间)

task_h_load - 代表“任务分层加载

既然一个任务可以嵌套在几棵树中,那么计算它的负载就不那么简单了...

static unsigned long task_h_load(struct task_struct *p){
    struct cfs_rq *cfs_rq = task_cfbs_rq(p);
    update_cfs_rq_h_load(cfs_rq);
    return div64_ul(p->se.avg.load_avg * cfs_rq->h_load,
                           cfs_rq_load_avg(cfs_rq) + 1);
}

开头 cfs_rq 指向找到 p 的直接树。如果我们只有两个嵌套树,那么计算 p 的负载会很简单:

task_h_load = task_load_in_its_tree * ( load_of_immediate_tree / load_of_containing_tree);

(while immediate_tree refers to the tree that contains the task, and containing_tree refers to the tree that contains the tree that contains the task.)

但事实并非如此。我们的树可能是调度实体中的嵌套树,它本身只是另一棵树中的一片叶子。

所以,我们做的第一件事就是调用 update_cfs_rq_h_load(cfs_rq),它计算 cfs_rq 及其所有上级(祖先)的层次负载因子:这个函数一直沿着树层次结构上升到根,然后从根向下到我们的 cfs_rq,同时计算层次结构中每棵树的层次负载因子。

计算方式类似:

cfs_rq_h_load = cfs_rq_load_in_its_tree * (load_of_immediate_tree / load_of_containing_tree)

所以,最后我们得到了 cfs_rq 的分数负载,我们所要做的就是使用相同的公式计算 h_load。

task_h_load = task_load_in_its_tree * (load_of_immediate_tree / load_of_containing_tree)