Linux内核实现了基于虚拟时钟的完全公平调度算法 .
每个调度实体都有一个与之关联的 sched_entity 结构,其快照如下所示
struct sched_entity {
...
u64 exec_start;
u64 sum_exec_runtime;
u64 vruntime;
u64 prev_sum_exec_runtime;
...
}
以上四个属性用于跟踪进程的运行时,并使用这些属性以及其他一些方法( update_curr() ,其中这些更新),实现虚拟时钟 . 将进程分配给CPU时, exec_start 将更新为当前时间,并且消耗的CPU时间将记录在 sum_exec_runtime 中 . 当从CPU取消进程 sum_exec_runtime 时,值保留在 prev_sum_exec_runtime 中 . sum_exec_runtime 是累积计算的 . (意思是它单调地增长) .
vruntime 存储流程执行期间虚拟时钟已经过的时间量 .
How vruntime is calculated?
无视所有复杂的计算,计算方法的核心概念是: -
vruntime += delta_exec_weighted;
delta_exec_weighted = delta_exec * (NICE_0_LOAD/load.weight);
这里 delta_exec 是分配给CPU并从CPU取消的进程之间的时间差,而 load.weight 是取决于优先级(Nice Value)的进程权重 . 通常,一个进程的nice值增加1意味着它可以减少10%的CPU时间,从而减轻重量 . 使用NICE值0,重量= 1024的过程重新处理值为1,重量= 1024 / 1.25 = 820(约)
Points drawn from above
所以当进程获得CPU时 vruntime 会增加
与较低优先级的进程相比,优先级较高的进程的
和 vruntime 缓慢增加 .
runqueue维护在红黑树中,每个runqueue都有一个与之关联的 min_vruntime 变量,它在运行队列中的所有进程中保存最小的 vruntime . ( min_vruntime 只能增加,而不会因为将安排进程而减少) .
红黑树中节点的关键是 process->vruntime - min_vruntime
调用调度程序时,内核基本上会选择具有最小密钥(最左边的节点)的任务并将其分配给CPU .
具有较小键的元素将更多地放置在左侧,因此可以更快地安排 .
当进程运行时,它的 vruntime 将稳定增加,因此它最终会在红黑树中向右移动 . 因为 vruntime 对于更重要的过程增加得更慢,所以它们也会向右移动得更慢,因此他们被安排的机会更大,更少重要的过程 - 按要求 .
如果进程休眠,其 vruntime 将保持不变 . 因为每个队列 min_vruntime 会在此期间增加,所以睡眠过程将在唤醒后更多地放在左边,因为密钥(如上所述)变小了 .
因此,如果没有CPU,则没有机会将饥饿作为优先级较低的进程,其最小值将会最小,因此密钥将最小,因此它会快速移动到树的左侧并因此进行调度 .