进程的相关概念
概述:
在Linux内核的五大组成部分进程管理、内存管理、设备驱动、文件系统、网络协议)中,进程管理是非常重要的一部分。它虽然不像内存管理、虚拟文件系统那样复杂,但作为五大内核组成部分的核心,进程管理与其它四个模块都有联系,对于理解内核的运作非常重要。因此,对于进程管理是必须要充分理解和掌握的。
所有的现代操作系统都能够同时运行若干进程,至少用户错觉上是这样。如果系统只有一个处理器,则在任意给定时刻,只有一个进程可以运行;在多处理器系统中(系统有多个处理器),则真正可以并行运行多个进程,可以并行运行进程的数目,取决于物理CPU的数目
为了达到在宏观上感觉并行操作的目的,即在感官上觉得计算机能够同时做几件事情需要以下“手段”以很短的时间间隔在系统运行的应用程序之间不停地切换。切换时间非常短,以至于用户无法察觉到短时间内的停滞,完全感觉不到不断切换的动作和过程。
程序与进程
什么是程序?
程序是存放在磁盘上的一系列代码和数据的可执行映像,是一个静态的实体。程序是指令的有序集合,是一个在时间上按照严格次序前后相继的操作序列。
什么是进程?
进程(process)就是处于执行期的程序(目标代码存放在某种存储介质上),其可以理解为程序执行的一个实例,也可以说进程是程序的一次执行,是一个动态的实体。但进程并不仅仅局限于一段可执行程序代码,通常还包括其他资源,如打开的文件、挂起的信号、内核内部数据、处理器状态、一个或多个具有内存映射的内存地址空间及一个或多个执行线程、以及用来存放全局变量的数据段等。从内核的角度看,进程也可以称为任务。实际上,完全可能存在两个或多个不同的进程执行的是同一个程序。
进程的特征
动态性:进程是程序的执行过程,具有不同的状态:存活、消亡、活动、阻塞等。
并发性:多个进程实体可以在一段时间间隔内同时运行
独立性:进程是资源分配的最小单位。各进程之间互相独立、互不干扰
异步性:各进程向前推进的速度是不可预知的,呈现一定随机性。
结构性:进程具有一定结构。它由程序段、数据段和控制结构等组成。
进程与线程
什么是线程?
简称线程(thread),是在进程中活动的对象。执行线程(thread of execution),内核调度的对象是线程,而不是进程。在传统的Unix系统中,一个进程只包含一个线程,但现在的系统中,包含多个线程的多线程程序已经司空见惯。Linux系统对线程和进程并不作特别区分,对于Linux而言,线程只不过是一种特殊的进程罢了进程是资源分配的最小单位;线程是调度的最小单位。
进程四要素
1.有一段程序供其执行。这段程序并不一定是某个进程所独有,可以与其它进程共用
2.有进程专用的系统堆栈空间
3.有task struct数据结构,即通常所说的进程控制块(PCB)。有了这个数据结构进程才能成为内核调度的一个基本单位,接受内核的调度。同时,该结构也记录着进程所占用的各项资源以及相关状态
4.有独立的用户空间
以上四个条件缺一不可
在Linux内核中,交互式进程和批处理进程统称为普通进程(与实时进程相对),内核对实时进程和普通进程采用了不同的调度策略。
实时进程相对简单,自Linux问世以来没有本质的变化。主要是先进先出算法FIFO和时间片轮转算法Round-Robin(RR),基于优先级调度。
普通进程更为复杂,需要兼顾交互式进程和批处理进程的需求,经过了多次革命性的变化。
三态模型
在操作系统原理中,一个进程从创建而产生到撤销直至消亡的整个生命周期,可以用一组状态加以刻画。根据三态模型,进程的生命周期可分为如下三种进程状态:
- 就绪态(ready):具备运行条件,等待系统分配处理器以便运行
- 运行(执行)态(running):内占有处理器正在运行
- 阻塞(等待)态(blocked):不具备运行条件,正在等待某个事件的完成
关于挂起就绪态和挂起阻塞态的理解
事实上,可能出现这样一种情况,由于进程被不断地创建,系统的资源已经不能满足进程运行的要求,这个时候就必须把某些进程挂起,对换到磁盘中,使之暂时不能参与进程调度,起到平滑系统负荷的目的。
挂起就绪态和挂起阻塞态在有的书中也被称为静止就绪态和静止阻塞态。相应地就绪态和阻塞态被称为活跃就绪态和活跃阻塞态。
进程的描述符
概述:
- 进程描述符即struct task struct,是进程管理中最重要的数据结构。这个结构是整个进程管理的核心,Linux内核涉及进程和程序的所有算法都围绕此数据结构建立,其定义在include/linux/sched.h中,
- task struct包含很多成员,将进程与各个内核子系统联系起来。由于此结构体非常巨大因此这里挑选其中较为重要的成员进行介绍。
_state与exit_state
进程状态可以分为“活动时”状态和“死亡后”状态。“活动时”状态对应 state“死亡后”状态对应exit state。
unsigned int _state
_state描述了进程当前的状态,其各个取值定义详见于include/linux/sched.h。
TASK RUNNING
进程是可执行的,它或者正在执行,或者在运行队列中等待执行。
TASK_RUNNING这一个状态对应操作系统原理三态模型中的两种状态:就绪和运行。
TASK_UNINTERRUPTIBLE
与TASK_INTERRUPTIBLE基本根同,只是处于等待中的进程即使只能待资源有效时才能被唤醒,而不能被信号以及中断唤醒。这个状态通常在进程必须在等待时不受干扰或等待事件很快就会发生时出现。由于处于此状态的任务对信号不做响应,所以较之可中断休眠状态,使用得较少。
TASK_UNINTERRUPTIBLE这一个状态也对应操作系统原理三态模型中的阻塞态
TASK_STOPPED
进程停止(中止)执行。通常这种状态发生在进程接收到SIGSTOP、SIGSTP、SIGTTINSIGTTOU等信号的时候。此外,在调试期间接收到任何信号,都会使进程进入这种状态进程接收到SIGCONT信号后,重新回到TASK_RUNNING状态:
TASK_STOPPED这一个状态也对应操作系统原理三态模型中的阻塞态。
TASK KILLABLE
Linux2.6.25引入的进程休眠状态。机制类似于TASK_UNINTERRUPTIBLE,但可以被致命信号(SIGKILL)唤醒。引入这个状态的原因是防止无法杀死的“霸王进程”产生。TASK_KILLABLE这一个状态也对应操作系统原理三态模型中的阻塞态。
TASK_TRACED
正处于被调试状态的进程。如使用GDB或者ptrace进行调试某个进程,这个进程就处于TASK_TRACED状态。
TASK_DEAD
进程退出时,state被设置为该状态
除了以上状态,还有TASK_NEW、TASK_PARKED、TASK_WAKEKILL、TASK_WAKING、TASK_NOLOAD、TASK_IDLE等状态,在这里不--详细讲解了
int exit state
EXIT_ZOMBIE
僵死(僵尸)状态。表示进程的执行被终止,但是父进程还没有发布waitpid系统调用来收集有关死亡的进程的信息。
EXIT_DEAD
僵死撤销状态。表示进程的最终状态,父进程已经使用wait4或waitpid系统调用来收集了信息,因此进程将由系统删除。
EXIT_TRACE
EXIT_ZOMBIE与EXIT_DEAD的组合,即EXIT_ZOMBIE | EXIT_DEAD
pid与tgid
pid_t pid
进程号。每一个进程在系统当中有一个唯一的标识,不单是用户进程,用户线程、内核线程都对应着一个task struct,也就意味着都有一个pid。
pid_t tgid
线程组的领头线程的pid值。Unix系统通过pid来标识进程,而Linux把不同的pid与系统中每个进程或线程关联。Unix程序员希望同一组线程具有共同的pid,遵照这个标准Linux引入线程组的概念。。一个线程组所有线程与领头线程具有相同的pid,存入tgid子字段,getpid()返回当前进程的tgid值而不是pid的值
mm与active mm
struct mm_struct *mm
进程用户空间描述结构指针。对于普通用户进程来说mm字段指向其虚拟地址空间的用户空间部分;而对于内核线程来说,该指针为空。
struct mm_struct *active mm
内核调度器在进程上下文切换的时候,会根据mm字段判断即将调度的进程是用户进程还是内核线程。由于内核线程之前可能是任何用户进程在执行,因此用户空间部分的内容本质上是随机的,内核线程决不能修改其内容,故将mm设置为NULL,而active mm设置为某个进程的mm。
fs与files
struct fs_struct *fs
文件必须由进程打开,进程要与文件系统交互,就必须有一些数据需要维护,比如每个进程自身的当前工作目录和根目录。fs_struct就用于此目的,每个进程描述符task_struct中的fs字段指向进程的fs_struct结构对象。
struct files_struct *files
进程用一个files_struct结构来记录其文件描述符的使用情况,files_struct结构称为用户打开文件表,表的地址存放于进程描述符task_struct中的files字段。
policy
unsigned int policy
该进程的调度策略。目前有以下几种:SCHED_NORMAL、SCHED_FIFO、SCHED_RR SCHED_BATCH、SCHED_IDLE、SCHED_DEADLINE.
prio、static_prio、normal_prio和rt _priority
Linux中采用了两种不同的优先级范围:一种是nice值,另一种是实时优先级。
有4种进程优先级:prio、static_prio、normal_prio和rt_priority。
int prio
普通进程的调度优先级。
系统根据prio选择调度类,有些情况需要暂时提高进程优先级。在0~(MAX_PRIO-1)之间取值(MAX PRIO定义为140)其中:0~(MAX_RT_PRIO-1)(MAX_RT_PRIO定义为100)属于实时进程范围MAX_RT_PRIO ~ MAX_PRIO-1属于非实时进程范围。数值越大,表示进程优先级越低。
int static_prio
普通进程的静态优先级。
普通进程的静态优先级,在进程启动时分配。静态优先级不会随时间改变,内核不会主动修改它。内核不保存nice值,而是通过PRIO_TO_NICE宏根据taskstruct->static prio计算得到。这个值可以通过nice/renice或者setpriority()修改。
nice值仍沿用Linux的传统,在-20~19之间变动,数值越大,进程的静态优先级越低nice是用户可维护的,但仅影响非实时进程的优先级。进程初始时间片的大小进决定于进程的静态优先级,这一点不论是实时进程还是非实时进程都一样,不过实时进程的static prio不参与优先级计算。
nice与static prio的关系如下:
static_prio = MAX_RT_PRIO + nice +20
内核定义了2个宏,用来完成这一转换:PRIO TO NICE和NICE TO PRIO。具体代码在include/linux/sched/prio.h中
内核使用[0,139]数值表示优先级,数值越低优先级越高。其中[0,99]给实时进程使用
[100,139]给普通进程使用。
用户空间nice传递的变量映射到普通进程优先级,即100~139
int normal_prio
普通进程的动态优先级。
normal_prio是基于静态优先级static_prio和调度策略计算出来的优先级,在创建进程时会继承父进程的normal_prio。可以通过 setscheduler函数来设置normal_prio的值 。对普通进程(非实时进程),normal_prio就等于static prio;对于实时进程,,会根据rt_priority重新计算normal_prio。
对于普通进程:normal_prio=static_prio;
对于实时进程:normal_prio=99-rt_priority=(MAX_RT_PRIO-1-rt_priority)
unsigned int rt_priority
实时进程的实时优先级。
实时进程的优先级,与进程设置参数sched_param.sched_priority等价。
实时优先级(rt_priority)在普通进程中等于0,实时进程中范围是[0,99]。rt_priority的值越大,意味着进程优先级越高。
rt_priority的值也是取决于调度策略的,可以在_setscheduler函数中对rt_priority的值进行设置。
范围归纳表
stack和thread info
void *stack和struct thread info thread info
task struct结构用于描述进程以及线程,称为进程描述符。而thread_info则是一个与进程描述符相关的小数据结构。对于每个进程,Linux都将thread_info与进程的内核态堆栈存放在一起,在一个单独为进程分配的内存区域中。
这块内存区域通常是8192字节(占两个页框),起始地址必须是8192的整数倍。而由于这个内存区域同时保存了thread_info和stack,因此这两个数据结构被定义在了一个共用体(联合体)中,由allocthread_info_node分配内存空间。
在2.6以前的内核版本中,各个进程的task_struct存放在他们内核栈的尾端,内核栈和task_struct一共占用2个连续的物理页。这样做是为了让那些寄存器较少的硬件体系结构(如x86)只要通过栈指针就能计算出它的位置,而避免使用额外的寄存器专门记录
随着内核的不断发展,task_struct结构的内容越来越多,其所占的空间也就越来越大,导致内核栈不断被压缩。为了解决这一问题,新版本内核引入了thread_info结构替代task_struct结构,利用其中的成员struct task_struct *task指向task_struct结构实例thread_info结构相对固定,因此,这样做实际上是将task_struct结构独立出来,不再受task_struct结构不断增长的影响了。
current
current并不在task_struct结构中,即不是task_struct的成员。在Linux中,用current指针指向当前正在运行的进程的task_struct。
在系统运行的时候,经常需要了解当前正在占有CPU而执行的是哪一个进程。在Linux内核中提供了一个全局变量current,其指向了正在运行的那一个进程或者线程的PCB,即task_struct结构。
进程的创建
毫无疑问,进程在被创建的时刻开始存活。在Linux系统中,这通常通过fork系统调用来实现。该系统调用通过复制一个现有进程来创建一个全新的进程。调用fork(的进程称为父进程,新产生的进程称为子进程。该调用结束时,在返回点这个相同位置上,父进程恢复执行,子进程开始执行。fork()系统调用从内核返回两次:一次回到父进程,另一次回到新产生的子进程。
通常,创建新的进程都是为了立即执行新的、不同的程序,而接着调用exec函数族就可以创建新的地址空间,并把新的程序载入其中。
也就是说,在Linux中,进程的创建是通过“fork()+exec()”的两阶段过程来实现的。系统调用fork()仅仅复制当前进程,子进程的内容和父进程的内容几乎完全一样(子进程与父进程的区别仅仅在于PID(每个进程唯一)、PPID(父进程的进程号,子进程将其设置为被拷贝进程的PID)和某些资源和统计量)。如果需要运行的是新的程序,则需要通过exec系统调用来完成,也就是说exec函数族负责读取可执行文件载入地址空间运行。
严格来说,fork()并不是一个系统调用,而是一组系统调用,Linux提供了3个创建进程的系统调用:fork0)、vfork0)和clone()。
fork: fork是重量级调用,因为它建立了父进程的一个完整副本。即子进程复制父进程的资源,父子进程拥有各自的地址空间。为了减少与fork系统调用相关的工作量,Linux使用了写时复制(拷贝)技术。…
vfork:类似于fork,但并不创建父进程数据的副本,即共享父进程的资源。子进程与父进程共享地址空间。子进程会将父进程阻塞,保证子进程先于父进程执行直到子进程退出或执行新程序。
clone:带有众多参数,可以精准控制复制进程时的各种行为(比如可以实现与fork和vfork完全相同的功能,也可以用来创建轻量级进程即线程)。通过clone标志创建子进程,不同标志可以实现不同的效果,具体标志含义见下表clone flag说明
线程的创建
线程的创建和普通进程的创建类似,只不过在调用cone()的时候需要传递一些参类
标志来指明需要共享的资源
clone(CLONE_VM |CLONE_FS |CLONE_FILES |CLONE_SIGHAND, 0);
创建的步骤
fork、vfork和clone系统调用的入口点分别是sys fork、sys vfork和sys clone函数,其实现依赖于具体的体系结构,因为用户空间和内核空间传递参数的方法因体系结构面异。原型分别如下(include/linux/syscalls.h中)
asmlinkage是通过汇编实现的
kernel_clone函数
(源码较长,略),主要完成的功能如下:
kernel clone函数的实现在kernel/fork.c中
- 首先,通过copy_process()复制父进程所有必需的数据结构,然后返回子进程的进程描述符并赋给局部变量p。
- 然后,get task_pid)将获取子进程的PID结构,进而通过pid_vnr()获取子进程在所属命名空间的进程ID并赋值给局部变量nr。
- 如果clone _lags设置了CLONE_PARENT_SETTID标志,那么nr会通过put_user()拷贝到parent_tidptr变量中。
- 如果clone_flags设置了CLONEVFORK标志,那么会初始化个completion变量并
- 赋值给子进程的vfork done字段。
- 接下来,父进程将通wake_up_new task唤醒刚刚创建的子进程。
- 唤醒之后,如果clone_flags设置了CLONE_VFORK标志,则父进程会通过wait_for_vfork_done()等待子进程执行新程序或者退出。
- 最后,返回nr。
copy_process函数
复制进程数据结构的工作由copy_process函数完成,这是进程创建的关键步骤。copy_process函数的实现同样在kernel/fork.c中(源码较长,略),主要完成的功能如下:
- 检查clone_flags参数中传递的标志是否合法(是否存在冲突)
- 调用dup_task_struct()为新进程创建一个内核栈、thread_info结构和task_struct这些值与当前进程的值相同。此时,子进程和父进程的描述符是完全相同的。
- 初始化 ftrace,以供内核追踪函数调用
- 初始化互斥锁。
- 拷贝父进程的信号。
- 检查当前用户的线程数是否大于最大线程数
- 初始化子进程链表及兄弟进程链表。
- 初始化自旋锁。
- 初始化挂起的信号。
- 初始化子进程调度策略,优先级,调度类等进程调度相关成员,
- 子进程拷贝父进程的所有进程信息(files、fs、sighand、signal、mm、namespaces、io、thread tls等)
- 手v:kunlun991
- 分配新的pid。
- 做一些清理工作。正常情况下,最终返回taskstruct;异常情况下,返回错误值。
copy_process函数结束并返回后,如果copy_process函数在执行过程中没有错误,则调用wake_up_new_task函数唤醒子进程并将其加入调度队列,此时状态为TASK RUNNING.
如果指定了CLONE VFORK标识,则要保证子进程先于父进程执行。具体方法是:把父进程加入等待队列,直到子进程释放自己的内存地址空间(子进程结束或执行新的程序)
至此,进程创建工作就全部完成了!
进程的销毁
进程销毁和进程创建是相对的。当一个进程终结时,内核必须释放它所占有的资源,并把这一“不幸事件”告知其父进程。
一般来说,进程的析构是其自身引起的。它发生在进程调用exit系统调用时,既可能显式地调用,也可能隐式地从某个程序的主函数返回(其实C语言编译器会在main函数的当进程接收到它既不能处理也不能忽略的信号或返回点后面放置调用exit()的代码)。异常时,它还可能被动地被终结。
更直观地说,Iinux进程退出的方式如下
正常退出
从main函数返回1(return)
调用exit
调用_exit
异常退出
调用abort
由信号终止
这里顺带提一下 exit与exit的区别和联系:
_exit()是linux系统调用,关闭所有文件描述符,然后退出进程,
exit()是C语言的库函数,其最终调用_exit(),但在此之前,先清洗标准输出的缓存,调用通过atexit注册的函数等
还有一个_Exit,它是C语言的库函数,自c99后加入,等价于_exit
退出方式
进程的调度
调度的基本概念
进程的数量往往多于处理器的个数,进程争用处理器的情况在所难免。进程调度就是从就绪队列中,按照一定的算法(公平、高效)选出最合适的一个进程并将处理器分配给它运行。
进程调度的本质是让进程更好地分时复用处理器资源。
核心知识点
调度策略
就绪进程有很多,为什么要选择某一个进程X,依据是什么?
调度时机
什么时候发生调度?进程1正在运行,现在要用进程2替换进程1,什么时候进行替换?
调度步骤
当发生调度的时候,怎么去完成调度,具体需要做哪些工作?
相关概念
时间片
时间片(TimeSlice)指的是分时复用过程中每个进程允许持续运行的最大时间配额单位。假设进程1持续运行了一个TimeSlice,那么它必须考虑让出CPU资源给其它进程不过有两点需要注意:一是进程持续运行时间可以小于TimeSlice(上文描述中强调了“最大”);二是进程持续运行时间也可以大于TimeSlice,比如当进程1的时间片耗尽,考虑让出CPU,但此时并没有其它可运行进程,那么进程1会持续运行(上文描述中强调了“单位”)
优先级
优先级(priority)指的是在所有进程中,谁更有资格优先获得处理器资源。一般来说现代的进程调度器是基于优先级的调度,也就是说倾向于先运行优先级高的进程,再运行优先级低的进程,同优先级之间轮转调度。不过凡事也有例外,也会存在“优先级倒挂(反转)”的情况,即某些高优先级进程挂起而低优先级进程运行的情况。
Linux内核中的优先级分为静态优先级和动态优先级(见前面章节)静态优先级一般是进程创建时确定的,也可以通过特定的系统调用改变;动态优先级的初始值取决于静态优先级,但是随着进程的运行不断低调整改变
调度策略选择进程时考量的是动态优先级
抢占调度
抢占调度(Preemptible Scheduling)指的是一个高优先级进程是否可以强行夺取低优先级进程的处理器资源。如果可以强行夺取,就是可抢占的调度。但要注意,抢占调度并非一个简单的“是”与“否”的命题,而更多地表现为“某些时候可以抢占,另外一些时候不可以抢占”。因此,根据可抢占的程度,大致可以将抢占调度分为完全不可抢占、用户态抢占和内核态抢占。
用户态抢占
Linux2.4以及更早版本内核的调度器只允许用户态抢占。用户态抢占是指当某个进程运行在用户态时,一个更高优先级的进程可以抢占该进程。然而,内核不可能随时随地地检测是否有更高优先级的进程产生,而是在特定的时间点检测,这些时间点称为检查点(CheckPoint)。对于用户态抢占,其检查点是异常、中断或系统调用处理完成后返回用户态的时候。允许用户态抢占是现代操作系统内核设计的底线,Linux内核配置选项中的“不抢占”(CONFIG PREEMPT NONE)实际上指的是用户态抢占。
内核态抢占
Linux2.6以及更新版本内核的调度器允许内核态抢占。内核态抢占是指某个进程不论是运行在用户态还是内核态,一个更高优先级的进程都可以抢占该进程的时间片。同样,随时随地的检查是不可能的,内核态抢占无非是在用户态抢占的基础上,又增加了更多的抢占检查点而已。一般来说,内核态抢占的检查点是异常、中断或系统调用处理完成后返回的时候。请注意,异常、中断或系统调用处理完成后,既可以返回到内核态,也可以返回到用户态,所以内核态抢占是用户态抢占的超集。
Linux的内核态抢占又可以细分为以下三种:
内核态自愿抢占
对应内核配置选项中的CONFIG_PREEMPT_VOLUNTARY。采用白名单机制,在用户态基础上增加了一些特定的内核态检查点,通过显式调用migh_ resched()来决定要不要调度。到更高优先级的进程。严格来讲,这不算真正意义上的内核抢占。
内核态完全抢占
对应内核配置选项中的CONFIG_PREEMPT。采用黑名单机制,除非是在临界区内,其它任意时刻都允许内核态抢占。临界区指的是访问临界资源的区间,具体来说包括关中断的区间(硬中断和软中断)以及preempt disable()和preempt enable()所包含的区间。Linux内核态抢占通常指的是这一种。
内核态实时抢占
对应内核配置选项中的CONFIG_PREEMPT_RT。这是最彻底的内核态抢占,取消了所有临界区,即便是在中断处理过程中也允许抢占。Linux内核从5.3版本开始加入这种模式的初步支持,但到目前为止PREEMPT_RT尚不完善
所有的调度器都围绕着时间片、优先级、抢占调度这三个基本概念进行设计。调度器之间的区别,无非是时间片的长短定义不一样;优先级的计算以及围绕优先级对进程的组织不一样;允许抢占的程度不一样。
调度策略
调度策略分为以下几种
SCHED_NORMAL:普通的分时进程。
SCHED_FIFO:先入先出的实时进程。
SCHED_RR:时间片轮转的实时进程。
SCHED_BATCH:批处理进程。
SCHED_IDLE:只在系统空闲时才能够被调度执行的进程
SCHED_DEADLINE:实现了 Earliest Deadline First (EDF) 调度算法。
SCHED_ISO:保留,尚未实现。
一个进程可以选择以上几种调度策略中的一种来参与调度。注意:调度策略是和进程对应起来的,进程会选择其中的一种来作为它的调度策略,而不是说整个系统都是使用先入先出或时间片轮转等。
调度类
调度类的引入增强了内核调度程序的可扩展性,这些类封装了调度策略,并将调度策略块化。调度类对应的数据结构struct _sched_class,其定义在kernel/sched/sched.h中由于结构体内容较多,因此只列举部分成员,如下所示:
公平调度类(CFS调度类)
在kernel/sched/fair.c中实现。用于以下调度策略:
SCHED_NORMAL、SCHED_BATCH.
实时调度类
在kernel/sched/rt.c中实现。用于以下调度策略:
SCHED_RR、SCHED_FIFO
实时调度类划分出来后,就产生了实时进程(线程)的概念。如果采用的策略是实时调度策略,那么该进程就是实时进程(线程)
空闲调度类
在kernel/sched/idle.c中实现。用于以下调度策略SCHED_IDLE
限期调度类
在kernel/sched/deadline.c中实现。用于以下调度策略:
SCHED_DEADLINE
停机调度类
在kernel/sched/deadline.c中实现。无对应调度策.