2、进程调度

多任务系统有两种类型:抢占式多任务和非抢占式多任务系统。
linux是抢占式内核。早期有非抢占式版本。
进程调度有两种算法:O(1)和反转楼梯最后期限调度算法。
O(1)算法:这个算法在交互式桌面上表现不佳,后来转为linux的交互式桌面提出了反转楼梯最后期限调度算法。
进程可分为I/O消耗型和CPU消耗型进程。

2-1、程序调度优先级

linux系统偏向于调度I/O消耗型进程,I/O消耗型进程的优先级比CPU消
耗型进程高。但并不是忽略cpu消耗型进程。
linux有设定两种优先级配置。一种时用nice值来设置优先级,
优先级越大,nice值越小。范围是-20到19,-20是最高优先级,
19是最低优先级。使用ps -el查看进程表,其中标记NI就是nice值。
第二种是实时优先级,ps -eo pid,rtprio,comm命令可以查看实时优先级。
这两个优先级相互不统筹。

2-2、时间片

时间片会影响I/O消耗型和cpu消耗型。
I/O消耗性不需要长时间片,cpu消耗型则越长越好,以增加高速缓冲的命中率。
linux并不是直接分配时间片到进程,而是直接分配处理器使用比例。
这样,每个进程所使用的的时间就和系统的负载密切相关。
如一个进程消耗的使用占比消耗的占比比当前的小,那么这个进程会被立刻执行。否则推迟运行。

一个简要例子:一个按键输入文字,一个视频处理程序。按键处理时间很短,程序运行的也很短,视频处理很长,当两者优先级一样时,各占百分之50处理器处理比重,当按键操作时,因为按键处理的时间短,所以会被立刻执行。按键处理肯定要优先处理,不然会让用户体验上不是很好。

2-3、调度器类

linux的调度器类是按模块提供的,允许不同的进程使用不同的调度器类,比如:CFS–完全公平调度器,针对普通进程,基础的调度器定义在kernel/sched.c中。linux的两种调度依靠CFS分配。

2-4、进程调度主要依靠四部分

2-4-1、时间记账

所有调度器都需要对进程进行时间记账。当每次系统时钟节拍发送变化时,都需要减少一个节拍时间,当减少为0时,进程就会被可运行的进程抢占。

2-4-2、进程选择

CFS调度器使用红黑树来组织可运行进程队列。宏黑树也称为自平衡二叉搜索树。

2-4-3、调度器入口

进程的调度器入口函数时schedule()。

2-4-4、休眠和唤醒

休眠是一个特殊的状态,如果没有这个状态,调度程序可能会选出一个没有意义的进程来执行。

过程:进程把自己标记成休眠状态,然后把自己从可执行红黑树中移除。放入到等待队列中,等待被唤醒。被唤醒后,进程被设置为可执行状态
,被放入到可执行红黑树中。被schedule()调用。

2-5、抢占和上下文切换

用户抢占发生情况:
含义:当一个用户空间进程在运行时,系统可通过调度器中断该进程,让更高优先级的进程优先执行。

  • 触发场景:
    进程主动放弃 CPU(如调用sleep等函数)。
    进程时间片耗尽。
    有更高优先级进程进入就绪状态(如实时进程唤醒)。
  • 实现基础:
    依赖 CPU 的中断机制和进程调度器(如 CFS 调度器)。

内核抢占
含义:内核代码在执行时,允许被更高优先级的任务中断,转而执行其他内核任务或用户进程。

  • 触发场景:

    内核中存在显式的抢占点(如调度器函数schedule())。
    内核代码执行时,若开启抢占(通过preempt_enable),
    且满足抢占条件(如更高优先级任务就绪)。

  • 关键点:

    需处理内核临界资源的互斥(如自旋锁),避免竞态条件。

总结:

用户抢占确保用户进程间的公平调度,提升响应速度。
内核抢占让内核能及时响应高优先级任务(如硬件中断处理),
避免内核代码长期占用 CPU,提升系统整体实时性和吞吐量。

2-6、实时调度策略

Linux的实时调度策略旨在为对时间敏感度较高的任务提供更可靠的响应和执行保证,主要有以下两种:

  • SCHED_FIFO
    • 特点:先进先出调度策略。一旦任务进入就绪状态,它将一直运行,直到完成或者被更高优先级的实时任务抢占。这种策略下,相同优先级的任务按照进入就绪队列的顺序依次执行,不会发生时间片轮转。
    • 适用场景:适用于那些对时间确定性要求极高、不希望被其他低优先级任务干扰的实时任务,如工业控制中的实时数据采集和处理任务。
  • SCHED_RR
    • 特点:时间片轮转调度策略。与SCHED_FIFO类似,具有较高优先级的任务会优先执行,但不同的是,相同优先级的任务会分配一个时间片,当时间片用完后,如果该任务还未执行完,它将被放回就绪队列尾部,等待下一次调度,以此实现相同优先级任务之间的公平轮转执行。
    • 适用场景:适用于那些需要在多个实时任务之间实现公平共享CPU资源,同时又有一定时间限制的场景,例如实时多媒体应用中的音频和视频流处理。

在Linux中,还可以通过chrt命令或者编程方式使用setpriority等函数来设置进程的调度策略和优先级,以满足不同实时应用的需求。

2-7、调度相关的系统调用

以下是Linux中与调度相关的常见系统调用及其功能:

  1. **sched_setscheduler**:为指定进程或线程设置调度策略和优先级。
  2. **sched_getscheduler**:获取指定进程或线程的当前调度策略。
  3. **sched_setparam**:设置指定进程或线程的调度参数(主要是优先级)。
  4. **sched_getparam**:获取指定进程或线程的调度参数。
  5. **sched_yield**:当前进程主动放弃CPU,让调度器选择其他进程运行。
  6. **sched_get_priority_max**:返回指定调度策略可使用的最大优先级。
  7. **sched_get_priority_min**:返回指定调度策略可使用的最小优先级。
  8. **sched_rr_get_interval**:获取采用SCHED_RR(轮转调度)策略的进程的时间片长度。
  9. **sched_setaffinity**:设置指定进程或线程的CPU亲和性,即限制其只能在某些CPU上运行。
  10. **sched_getaffinity**:获取指定进程或线程的CPU亲和性掩码。

之前的设置
nice值也可以设置,如**nice()**
这里很多,不举例。

总的来说,有调度相关的,优先级相关的,处理器绑定的,放弃处理器的调度的相关系统调用。