我试图了解load_balance
函数期间发生了什么.
我正在检查版本3.14,但我也看了4.3版,因为我被告知机制已经改变,并且在这个版本中更加清晰.
在v3.14中,呼叫来自move_tasks
在v4.3中,呼叫来自detach_tasks
从我看来它是相同的功能,但只有不同的名称.
此功能根据env->balance
参数将任务从一个队列移动到另一个队列.
我不明白的是负载的计算方式和方式task_h_load
.
有谁知道加载成员代表什么以及如何在task_h_load
函数中计算它?
如果配置了SMP,则调用负载均衡.Linux使用完全公平调度程序(CFS)来安排每个任务,以便每个任务获得处理器时间的"公平"份额.CFS使用红黑树的概念.
调度程序记录任务进入运行队列的当前时间.当进程等待处理器时间时,它的"等待"值会增加从当前运行队列中的任务总数和进程优先级得出的数量.当处理器运行此任务时,此"等待"值将减少.如果此值低于某个值,则调度程序将抢占该任务,而其他任务则更接近处理器执行.CFS总是试图通过保持"等待"值为零来保持情况的理想状态.
在Linux中两个函数load_balance
和select_task_rq_fair()
它进行负载均衡的任务.
简单来说,CFS负载均衡机制将繁忙的CPU卸载到不太繁忙或理想的CPU.
task_h_load
用于计算任务的重量.
我不明白的是在task_h_load中计算负载的方式/方式.如何在task_h_load函数中计算?
该权重因子取决于该过程的良好价值.
weighting factor = weight of process with nice value 0 / weight of current task;
其中' weight '约等于1024*(1.25)^( - nice)
例如:重量为820表示良好值1重量为1277表示良好值-1
task_h_load
有关更多负载平衡方法和基础知识,请参阅内核注释
task_h_load
用于 update_cfs_rq_h_load
计算运行队列的分层负载以进行公平时间调度,以及使用cfs_rq_load_avg返回runqueue load_avg的平均负载.
虚拟运行时间是任务在CPU上运行的加权时间.CFS总是尽量保持这个红黑树的平衡.
< - 较小的值----------- vruntime值--------较大的值 - >
每个可运行的任务都放在基于自平衡红黑树的位置vruntime
.如果任务已准备好运行(表示任务不等待任何资源),则将其置于树中.如果任务正在等待某些资源(即等待I/O),则将其删除.具有较少处理时间(意味着较小vruntime
)的任务朝向树的左侧,具有更多处理时间的任务是树的右侧.
左节点具有最小的键值(对于CFS,它是具有更高优先级的任务).自平衡红黑树需要O(lgN)操作以向左节点导航.调度程序缓存此值rb_leftmost
.通过仅检索此值,调度程序确定下一个要运行的任务
此负载平衡仅用于非实际任务,仅用于由Steven Rostedt和Gregory Haskins开发的实时任务推拉操作
关于CFS还有一件事在公平组调度中也很有用.请参考下图
move_task
只是尝试将不平衡加权负载(在计算如上所示的负载系数后的平均值)从忙碌上升到this_rq
.它试图等于两个运行队列的负载平衡,这样两者都可以节省"公平"的处理器时间.
detach_task
分离env
linux内核指针中指定的迁移任务.
detach_one_task
试图从env-> src_rq中正好出列一个任务.
detach_tasks()
试图分离加权负荷不平衡
busiest_rq
.并且它返回分离任务的数量,否则如果分离失败则为零.
要将此分离任务附加到新rq
(运行队列),请attach_task, attach_one_task,attach_tasks
根据情况使用.
lockdep_assert_held()
引入了新的警告检查,detach_tasks
其中没有move_tasks
在多处理器上,如此轻松地移动任务并不容易,因此cfs执行特定于域的负载平衡,如下所示:
为了解所有这些,我希望您通过以下参考
用于负载平衡的每实体负载跟踪度量标准
权重因子
开放式的SUSE
罗伯特爱第3章
CPU调度
我特意阅读了所有这些文档来回答你的问题我希望你不介意评论是否缺少某些东西.
CFS拥有一个"调度实体"树.每个调度实体都可以拥有自己的树,依此类推......(例如,这对于将特定用户的所有进程分组到一个调度实体中非常有用;从而防止具有许多任务的用户比具有较少进程的用户消耗更多的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);
(而immediate_tree是指包含任务的树,contains_tree是指包含包含任务的树的树.)
但这种情况并非如此.我们的树可能是调度实体内的嵌套树,它本身只是另一棵树中的叶子.
因此,我们要做的第一件事就是调用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)