进程优先级
1. 基本概念
CPU 资源分配的先后顺序,就是指进程的优先权。优先权高的进程有优先执行的权利。配置进程优先权对多任务环境的 Linux 很有用,可以改善系统性能。此外,还可以通过设置CPU亲和性,把进程运行到指定的CPU上,这样一来,把不重要的进程安排到某个CPU,可以改善系统整体性能。
2. 查看系统进程
Linux 系统下可以用 ps -al 命令输出以下内容:
- UID:代表执行者的身份。
- PID:代表这个进程的代号。
- PPID:代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号。
- PRI:代表这个进程可被执行的优先级,其值越小越早被执行。
- NI:代表这个进程的 nice 值。
3. PRI and NI
- PRI:代表进程的优先级,
PRI越小执行越快。 - NI:表示进程优先级的修正数值。
引入 NI 后,PRI(new) = PRI(old) + NI,所以调整进程优先级就是调整 NI。
NI 的取值范围是 -20 到 19,一共 40 个级别。
4. 指令修改进程优先级
top -> r -> 输入进程 ID -> 输入 NI 值nicerenice
5. 进程切换
进程发生上下文切换的时候:
- 内核栈:在上下文切换时,用于保存和恢复寄存器状态。
- 用户堆栈:在上下文切换时保持不变,用于保存用户程序的函数调用栈、局部变量、返回地址和临时数据。
6. Linux 2.6 内核进程 O(1) 调度队列
O(1) 调度器使用了双向链表和位图的组合来实现高效的进程调度。
运行队列(runqueue)组成
-
元数据:包含锁(
lock)、运行进程数(nr_running)、CPU 负载因子(cpu_load)等,用于调度控制和状态统计。 -
双队列设计:
- 活跃队列(
array[0]) :存储时间片未耗尽的进程,通过nr_active(活跃进程数)、bitmap[5](位掩码,快速标记非空优先级队列)、queue[140](140 个优先级链表,同优先级进程按 FIFO 排列)管理。 - 过期队列(
array[1]) :存储时间片耗尽的进程,结构与活跃队列相同,通过active和expired指针轮换(活跃队列为空时交换,重新分配时间片,避免进程饥饿)。
- 活跃队列(
优先级与队列管理
- 优先级范围:
0~139(0~99实时优先级,100~139普通优先级,对应nice值-20~19)。 - 队列管理:每个优先级对应一个双向链表,进程按优先级分组,同优先级按 FIFO 调度。
- 位图结构:
bitmap[5]由 5 个unsigned long(共 160 位)组成,每一位对应一个优先级队列的空满状态。通过位运算实现 O(1) 查找,提升调度效率。
调度流程
调度器的工作流程如下:
调度决策
- 调度器通过
active->bitmap快速定位最高优先级非空队列(如idx=5),取出队首进程执行。 - 进程时间片耗尽后,从
active->queue[idx]移除,加入expired->queue[idx],并标记expired->bitmap[idx]为非空。
队列交换
- 当
active->nr_active减至 0 时,内核自动执行swap(&active, &expired),交换指针指向。 - 交换后,原
expired队列成为新的active队列,原active队列变为expired队列。
新进程加入
- 新进程直接加入
expired队列。 - 时间片耗尽的进程从
active队列移入expired队列。
优先级和时间片管理
优先级范围:0~139,其中 0~99 为实时优先级,100~139 为普通优先级。
时间片分配:
每个进程在进入活跃队列时被分配一个时间片。时间片的长度取决于进程的优先级。优先级越高的进程(数值越小)获得的时间片越长。
时间片耗尽:
当一个进程用尽其时间片时,它会被移到过期队列中。
优先级调整:
通过NI优先级调整,但不会立即影响当前的时间片。进程会继续运行当前的时间片,直到时间片耗尽。当进程的时间片耗尽时,它会被移到过期队列中。在过期队列中,进程会根据新的优先级等待下一次队列交换。