接前一篇文章:Linux内核进程管理子系统有什么第七十四回 —— 进程调度(1)
本文内容参考:
Linux内核进程管理专题报告_linux rseq-优快云博客
《趣谈Linux操作系统 核心原理篇:第三部分 进程管理》—— 刘超
《图解Linux内核 基于6.x》 —— 姜亚华 机械工业出版社
https://zhuanlan.zhihu.com/p/19845213825
特此致谢!
上一回开始讲解进程调度,讲了进程调度的核心知识点(包括调度策略、调度时机、调度步骤)以及相关概念。上一回讲解了时间片和优先级,本回继续讲解进程调度的相关概念。
进程调度的相关概念
- 调度策略
进程的调度策略(Policy)目前包括以下几种:
SCHED_NORMAL(普通分时进程)、SCHED_FIFO(先入先出实时进程)、SCHED_RR(时间片轮转实时进程)、SCHED_BATCH(批处理进程)、SCHED_IDLE(只在系统空闲时才能够被调度执行的进程,即空闲进程)、SCHED_DEADLINE(实现了 Earliest Deadline First (EDF) 调度算法)。
这些定义在include/uapi/linux/sched.h中,如下:
/*
* Scheduling policies
*/
#define SCHED_NORMAL 0
#define SCHED_FIFO 1
#define SCHED_RR 2
#define SCHED_BATCH 3
/* SCHED_ISO: reserved but not implemented yet */
#define SCHED_IDLE 5
#define SCHED_DEADLINE 6
一个进程可以选择以上几种调度策略中的一种来参与调度。
这里注意:调度策略是和进程对应起来的,进程会选择其中的一种来作为它的调度策略,而不是说整个系统都是使用先入先出或时间片轮转等。
- 抢占调度
抢占调度(Preemptible Scheduling)指的是一个高优先级进程是否可以强行夺取低优先级进程的处理器资源。如果可以强行夺取,就是可抢占的调度。
但要注意,抢占调度并非一个简单的“是”与“否”的命题,而更多地表现为“某些时候可以抢占,另外一些时候不可以抢占”。因此,根据可抢占的程度,大致可以将抢占调度分为完全不可抢占、用户态抢占和内核态抢占。
1)完全不可抢占
完全不可抢占的调度器称为协作式调度,进程1切换到进程2的唯一情况是进程1主动放弃。几乎不可能有完全不可抢占调度的现代操作系统内核,因为系统级的完全不可抢占是很致命的,一个简单的无限循环的应用程序就可以导致系统死掉。
2)用户态抢占
Linux2.4及更早版本内核的调度器只允许用户态抢占。用户态抢占是指当某个进程运行在用户态时,一个更高优先级的进程可以抢占该进程。然而,内核不可能随时随地地检测是否有更高优先级的进程产生,而是在特定的时间点检测,这些时间点称为检查点(CheckPoint)。对于用户态抢占,其检查点是异常、中断或系统调用处理完成后返回用户态的时候。允许用户态抢占是现代操作系统内核设计的底线,Linux内核配置选项中的“不抢占”(CONFIG_PREEMPT_NONE)实际上指的是用户态抢占。

上图一中,假设周期性中断发生在进程A用户空间,此时进入到内核空间,在周期性调度器实现函数中设置了进程A的TIF_NEED_RESCHED标记位,则在时钟中断处理程序返回用户空间前夕,将调用schedule()函数执行进程调度。
上图二中,假设周期性中断发生在进程A在内核空间运行之时,时钟中断返回前并不会执行进程调度,因为这时返回的是内核空间,而不是用户空间。在进程A从内核空间返回用户空间前的工作中,才会检测TIF_NEED_RESCHED标记位,置位则执行进程调度。

3)内核态抢占
Linux2.6及更新版本内核的调度器允许内核态抢占。内核态抢占是指某个进程不论是运行在用户态还是内核态,一个更高优先级的进程都可以抢占该进程的时间片。同样,随时随地的检查是不可能的,内核态抢占无非是在用户态抢占的基础上,又增加了更多的抢占检查点而已。一般来说,内核态抢占的检查点是异常、中断或系统调用处理完成后返回的时候。请注意,异常、中断或系统调用处理完成后,既可以返回到内核态,也可以返回到用户态,所以内核态抢占是用户态抢占的超集。

上图一中,假设周期时钟中断发生在进程A用户空间,此时的处理与不支持内核抢占时相同,在中断处理程序返回用户空间前夕的工作中,执行进程调度。
上图二中,假设周期时钟中断发生在进程A在内核空间运行时,在时钟中断处理程序返回内核空间前的工作中就可能会执行进程调度(抢占计数需为0)。如果在中断处理程序返回内核空间前没有执行进程调度,则在返回用户空间前执行,与不支持内核抢占时相同。

Linux的内核态抢占又可以进一步细分为以下三种:
a)内核态自愿抢占
对应内核配置选项中的CONFIG_PREEMPT_VOLUNTARY。采用白名单机制,在用户态基础上增加了一些特定的内核态检查点,通过显式调用might_resched()来决定要不要调度到更高优先级的进程。严格来讲,这不算真正意义上的内核抢占。
b)内核态完全抢占
对应内核配置选项中的CONFIG_PREEMPT。采用黑名单机制,除非是在临界区内,其它任意时刻都允许内核态抢占。临界区指的是访问临界资源的区间,具体来说包括关中断的区间(硬中断和软中断)以及preempt_disable()和preempt_enable()所包含的区间。Linux内核态抢占通常指的是这一种。
c)内核态实时抢占
对应内核配置选项中的CONFIG_PREEMPT_RT。这是最彻底的内核态抢占,取消了所有临界区,即便是在中断处理过程中也允许抢占。Linux内核从5.3版本开始加入这种模式的初步支持,但到目前为止PREEMPT_RT尚不完善。
所有的调度器都围绕着时间片、优先级、抢占调度这三个基本概念进行设计。调度器之间的区别,无非是时间片的长短定义不一样;优先级的计算以及围绕优先级对进程的组织不一样;允许抢占的程度不一样。
进程调度相关概念就先介绍到这里了,下一回正式开始结合内核代码深入讲解进程调度。
925

被折叠的 条评论
为什么被折叠?



