我已经读过linux内核包含许多调度类,每个调度类都有自己的优先级。要选择要运行的新进程,进程调度程序将从最高优先级类迭代到最低优先级类。如果在类中找到可运行的进程,则选择优先级最高的进程从该类运行。
Robert Love从Linux内核开发中提取:
进程计划的主要切入点是功能 schedule(),在kernel / sched.c中定义。这是函数 内核的其余部分用于调用进程调度程序,决定 运行然后运行它的进程。 schedule()是通用的 尊重调度程序类。也就是说,它找到了最高优先级 具有可运行进程的调度程序类,并询问下一步运行什么。 鉴于此,schedule()很简单就不足为奇了 只是功能的重要部分 - 否则也是如此 在这里重现无趣 - 是它调用pick_next_task() ,也在kernel / sched.c中定义.pick_next_task()函数去了 通过每个调度程序类,从最高优先级开始,和 选择优先级最高的类中的最高优先级进程。
让我们假设以下场景。有些进程在较低优先级的类中等待,并且进程正在连续地添加到更高优先级的类中。低优先级的流程不会饿死吗?
答案 0 :(得分:1)
它确实会饿死。 有很多方法可以处理这种情况。
答案 1 :(得分:1)
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
存储流程执行期间虚拟时钟已经过的时间量。
如何计算vruntime
?
忽略所有复杂的计算,计算方法的核心概念是: -
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(大约)
从上方抽取的点数
vruntime
会增加
vruntime
会缓慢增加。 runqueue维护在红黑树中,每个runqueue都有一个与之关联的min_vruntime
变量,它在运行队列中的所有进程中保存最小的vruntime
。 (min_vruntime
只能增加,而不是随着进程的安排而减少。
红黑树中节点的键是process->vruntime - min_vruntime
当调用调度程序时,内核基本上会获取具有最小密钥(最左边的节点)的任务并将其分配给CPU。
较小键的元素将放置在左侧,因此可以更快地安排。
vruntime
将稳定增加,因此它最终将在红黑树中向右移动。
由于vruntime
对于更重要的流程而言增长速度更慢,因此它们也会向右移动得更慢,因此对于不太重要的流程来说,他们安排的机会更大 - 正如所需的那样。vruntime
将保持不变。因为每个队列min_vruntime
会在此期间增加,所以睡眠过程将在唤醒后更多地放在左边,因为密钥(如上所述)变小了。因此,如果被剥夺CPU,则没有机会将饥饿作为优先级较低的进程,其vruntime
最小,因此密钥最小,因此它会快速移动到树的左侧并因此进行调度。 / p>