深入Linux内核架构-进程管理和调度(十)

一、完全公平调度类

核心调度器必须知道的有关完全公平调度器的所有信息,都包含在fair_sched_class中:

kernel/sched_fair.c

在先前的讨论中,已经看到主调度器调用这些函数,接下来将考察这些函数在CFS(完全公平调度)中的实现方式。

1、数据结构

首先,需要介绍一下CFS的就绪队列。回想一下,可知主调度器的每个就绪队列中都嵌入了一个该结构的实例:

kernel/sched.c

各个成员的语义如下。

nr_running计算了队列上可运行进程的数目, load维护了所有这些进程的累积负荷值。

min_vruntime跟踪记录队列上所有进程的最小虚拟运行时间。这个值是实现与就绪队列相关的虚拟时钟的基础。其名字很容易会产生一些误解,因为min_vruntime实际上可能比最左边的树结点的vruntime大些。因为它是单调递增的。

tasks_timeline是一个基本成员,用于在按时间排序的红黑树中管理所有进程。rb_leftmost总是设置为指向树最左边的结点,即最需要被调度的进程。该成员理论上可以通过遍历红黑树获得,但由于通常只对最左边的结点感兴趣,因为这可以减少搜索树花费的平均时间。

curr指向当前执行进程的可调度实体。

2、CFS操作

现在把注意力转向如何实现完全公平调度器提供的调度方法。

1)虚拟时钟

完全公平调度算法依赖于虚拟时钟,用以度量等待进程在完全公平系统中所能得到的CPU时间。但数据结构中任何地方都没找到虚拟时钟!这是由于所有的必要信息都可以根据现存的实际时钟和与每个进程相关的负荷权重推算出来。所有与虚拟时钟有关的计算都在update_curr中执行,该函数在系统中各个不同地方调用,包括周期性调度器之内。图2-17的代码流程图提供了该函数所完成工作的概述。

kernel/sched_fair.c

首先,该函数确定就绪队列的当前执行进程,并获取主调度器就绪队列的实际时钟值,该值在每个调度周期都会更新( rq_of是一个辅助函数,用于确定与CFS就绪队列相关的struct rq实例):

kernel/sched_fair.c
 

如果就绪队列上当前没有进程正在执行,则无事可做。否则,内核会计算当前和上一次更新负荷统计量时两次的时间差,并将其余的工作委托给__update_curr。

kernel/sched_fair.c

 根据这些信息, __update_curr需要更新当前进程在CPU上执行花费的物理时间和虚拟时间。物理时间的更新比较简单,只要将时间差加到先前统计的时间即可:

kernel/sched_fair.c

如何使用给出的信息来模拟不存在的虚拟时钟。这一次内核的实现是非常巧妙的,针对最普遍的情形节省了一些时间。对于运行在nice级别0(数值越小,优先级越高)的进程来说,根据定义虚拟时间和物理时间是相等的。在使用不同的优先级时,必须根据进程的负荷权重重新衡定时间

kernel/sched_fair.c

忽略舍入和溢出检查, calc_delta_fair所作的就是根据下列公式计算:

越重要的进程会有越高的优先级(即,越低的nice值),会得到更大的权重,因此累加的虚拟运行时间会小一些。根据公式可知, nice 0进程优先级为120,则虚拟时间和物理时间是相等的,即current->load.weight等于NICE_0_LOAD的情况。

最后,内核需要设置min_vruntime。必须小心保证该值是单调递增的。

kernel/sched_fair.c

first_fair是一个辅助函数,检测树是否有最左边的结点,即是否有进程在树上等待调度。如果有,则内核获取其vruntime,即树中所有结点最小的vruntime值。如果因为树是空的而没有最左边的结点,则使用当前进程的虚拟运行时间。为保证每个队列的min_vruntime是单调递增的,内核将其设置为二者中的较大者。这意味着,每个队列的min_vruntime只有被树上某个结点的vruntime超出时才更新。

利用该策略,内核确保min_vrtime只能增加,不能减少。

完全公平调度器的真正关键点是,红黑树的排序过程是根据下列键进行的:

kernel/sched_fair.c

键值较小的结点,排序位置就更靠左,因此会被更快地调度。用这种方法,内核实现了下面两种对立的机制。

(1) 在进程运行时,其vruntime稳定地增加,它在红黑树中总是向右移动的。

因为越重要的进程vruntime增加越慢,因此它们向右移动的速度也越慢,这样其被调度的机会要大于次要进程,这刚好是我们需要的。

(2) 如果进程进入睡眠,则其vruntime保持不变。因为每个队列min_vruntime同时会增加,那么睡眠进程醒来后,在红黑树中的位置会更靠左,因为其键值变得更小了。

2、延迟跟踪

内核有一个固有的概念,称为良好的调度延迟,保证每一个可运行的进程都应该至少运行一次的某个时间间隔。它在sysctl_sched_latency给出,可通过/proc/sys/kernel/sched_latency_ns控制,默认值为20 000 000纳秒或20毫秒。第二个控制参数sched_nr_latency,控制在一个延迟周期中处理的最大活动进程数目。如果活动进程的数目超出该上限,则延迟周期也成比例地线性扩展。sched_nr_latency可以通过sysctl_sched_min_granularity间接地控制,后者可通过/proc/sys/kernel/sched_min_granularity_ns设置。默认值是4 000 000纳秒,即4毫秒,每次sysctl_sched_latency/sysctl_sched_min_granularity之一改变时,都会重新计算sched_nr_latency。

__sched_period确定延迟周期的长度,通常就是sysctl_sched_latency,但如果有更多进程在运行,其值有可能按比例线性扩展。在这种情况下,周期长度是:

通过考虑各个进程的相对权重,将一个延迟周期的时间在活动进程之间进行分配。对于由某个可调度实体表示的给定进程,分配到的时间如下计算:

kernel/sched_fair.c

回想一下,就绪队列的负荷权重是队列上所有活动进程负荷权重的累加和。结果时间段是按实际时间给出的,但内核有时候也需要知道等价的虚拟时间。

kernel/sched_fair.c

回想一下,对权重weight的进程来说,实际时间段time对应的虚拟时间长度为:

该公式也用于转换分配到的延迟时间间隔。

3、队列操作

有两个函数可用来增删就绪队列的成员:enqueue_task_fair和dequeue_task_fair。首先关注如何向就绪队列放置新进程。

kernel/sched_fair.c

除了指向所述的就绪队列和task_struct的指针外,该函数还有另一个参数wakeup。这使得可以指定入队的进程是否最近才被唤醒并转换为运行状态(在这种情况下wakeup为1),还是此前就是可运行的(那么wakeup是0)。enqueue_task_fair的代码流程图如图2-20所示。

如果通过struct sched_entity的on_rq成员判断进程已经在就绪队列上,则无事可做。否则,具体的工作委托给enqueue_entity完成,其中内核会借机用updater_curr更新统计量。

如果进程最近在运行,其虚拟运行时间仍然有效,那么它可以直接用__enqueue_entity加入红黑树中。该函数需要一些处理红黑树的机制,但这可以依靠内核的标准方法,无需多虑。函数的要点在于将进程置于正确的位置,这可以通过以下两点保证:此前已经设置过进程的vruntime字段,内核会不断更新队列的min_vruntime值。

如果进程此前在睡眠,那么在place_entity中首先会调整进程的虚拟运行时间:

kernel/sched_fair.c

函数根据initial的值来区分两种情况。只有在新进程被加到系统中时,才会设置该参数,但这里的情况并非如此: initial是零。

由于内核已经承诺在当前的延迟周期内使所有活动进程都至少运行一次,队列的min_vruntime用作基准虚拟时间,通过减去sysctl_sched_latency,则可以确保新唤醒的进程只有在当前延迟周期结束后才能运行。

但如果睡眠进程已经累积了比较大的不公平值(即se_vruntime值比较大),则内核必须考虑这一点。如果se->vruntime比先前计算的差值更大,则将其作为进程的vruntime,这会导致该进程在红黑树中处于比较靠左的位置,具有较大vruntime值的进程可以更早调度执行。

回到enqueue_entity:在place_entity确定了进程正确的虚拟运行时间之后,则用__enqueue_entity将其置于红黑树中。在此前已经注意到,这是个纯粹机械性的函数,它使用了内核的标准方法将进程排序到红黑树中。

kernel/sched_fair.c

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值