内核调度时机

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/rikeyone/article/details/87691208

调度的时机

内核中常见的调度场景如下:
(1)进程被阻塞时,比如执行sleep()后,需要立刻执行schedule()进行调度
(2)当唤醒进程时,比如try_to_wake_up()执行时,会重新计算负载,查找需要执行的进程,并设置TIF_NEED_RESCHED标志延时调度
(3)当周期执行的scheduler_tick()发现需要切换进程时,设置TIF_NEED_RESCHED标志延时调度

以上的场景只是个别举例介绍,那么概括起来的话,到底在哪些时间点是内核允许调度的时机呢?
调度发生的时机有如下一些时间点:
(1)显式调用schedule()
(2)中断和异常返回到userspace时,会判断current进程的TIF_NEED_RESCHED标志,如果置位,就触发调度行为。
(3)syscall系统调用返回到userspace时,会判断current进程的TIF_NEED_RESCHED标志,如果置位,触发调度行为。
(4)如果使能了内核抢占,那么在preempt_enable()被调用是也会判断TIF_NEED_RESCHED标志并且符合规则后触发调度行为。

延迟调度

直接调度就不用多做介绍了,上面已经提到过,直接显式调用schedule()函数就会触发调度行为,而内核中大部分的内核路径采用的是延迟调度的方式。
所谓延迟调度也就是,我们并不是立刻执行schedule()函数对进程进行调度,而是设置TIF_NEED_RESCHED标志位,当调度时机到来时,检测此标志位,进而调用schedule()进行调度行为。
前面介绍的过什么情况下会检测TIF_NEED_RESCHED标志并进行调度,而设置TIF_NEED_RESCHED标志的地方就是延迟调度发生的地方。

比如:
(1)周期调度函数scheduler_tick()中判断是否满足进程切换的条件,并设置延迟调用的标志位,也就是TIF_NEED_RESCHED标志。
(2)当使用try_to_wake_up唤醒一个进程时,如果唤醒进程的优先级高于当前正在执行的进程时,设置current进程的TIF_NEED_RESCHED标志。
(3)当系统调用sched_setscheduler()时,同样设置current进程的TIF_NEED_RESCHED标志。

为什么要存在延迟调度的方式,通过设置一个TIF_NEED_RESCHED,等到中断/异常返回的时候才执行,而不是直接执行调度行为?
原因如下:
(1)唤醒操作经常在中断上下文中执行,在这个环境中直接调用schedule()进行调度是不行的;
(2)为了维护非抢占内核以来的一些传统,不要轻易中断进程的处理逻辑除非他主动放弃;
(3)在普通上下文中,唤醒后接着调用schedule()也是可以的,比如smp_send_reschedule()、resched_curr()函数就是这么实现。

关键函数

  • schedule()
  • scheduler_tick()
  • try_to_wake_up()

猜你喜欢

转载自blog.csdn.net/rikeyone/article/details/87691208