进程优先级概念
CPU 资源有限,运行队列中的进程无法同时获得执行机会。因此,CPU 分配资源的先后顺序即定义为进程的优先级。
查看优先级信息
使用 ps -l 命令可查看系统中更详细的进程信息。其中与进程优先级直接相关的是 PRI 与 NI,它们作为整型成员存在于 task_struct 结构中。
- PRI:代表进程可被执行的优先级,数值越小越早被执行。
- NI:代表进程的 nice 值,用于修正优先级。
PRI 与 NI 的理解
PRI 直观反映程序被 CPU 执行的先后顺序,此值越小优先级越高。NI 则是优先级的修正数值。两者关系如下:
进程 PRI 值 = 80 + 进程 nice 值
nice 值变小,PRI 随之变小,进程优先级变高;反之亦然。因此,在 Linux 下调整进程优先级,本质就是调整 nice 值。nice 值的取值范围是 -20 到 19,共 40 个级别,对应的 PRI 范围为 [60, 99]。
修改 nice 值
可通过 top 命令动态修改已存在进程的 nice 值。进入 top 界面后输入 r,指定进程 PID,然后输入新的 nice 值即可。注意,若输入值超出规定范围(如大于 19),系统会自动截断至最大值 19。此时 PRI 将变为 99。实际生产中不建议高频更改 nice 值。
进程调度切换
Linux 为每个进程分配时间片,时间片耗尽后自动让出 CPU。由于 CPU 寄存器由所有进程共享,每次切换需保存当前上下文数据至 task_struct,并加载下一个进程的临时数据。这种机制属于分时操作系统,也是当代计算机的主流形态。
系统如何高效调度?以下解析 Linux 2.6 内核的 O(1) 调度实现算法。
list_head 与 prio_array 结构
在 task_struct 定义中,有两个关键字段:prio_array_t *array 和 run_list。array 指向当前进程所属的优先级数组指针,标识进程位于活跃队列还是过期队列。
struct list_head 是双向链表结构,run_list 构成了 CPU 运行队列的核心。分析 prio_array_t 结构可知,根据优先级选择进程队列的过程本质上是一种哈希映射。
虽然队列节点是 list_head 类型,看似不包含进程信息,但内核利用'容器之'(container_of)技巧解决了这一问题。假设知道结构体内部成员 a 的地址,且已知其在结构体中的偏移量,即可反推出结构体变量的起始地址。因此,队列中的每一个 list_head 实际上就是进程 task_struct 的成员变量 run_list。通过计算偏移量,内核能获取完整的进程信息。这种将链表节点嵌入目标结构体的设计,避免了额外内存开销,是 Linux 内核的经典实践。
选取合适进程时,最简单的做法是从下标 0 开始遍历 queue,找到第一个非空队列。由于 queue 大小固定为 140,该算法时间复杂度为 O(1)。为了进一步提升效率,prio_array 中还包含一个 bitmap 位图数组,用 140 个比特位标记 140 个进程队列是否为空,利用位运算大幅提高了查找非空队列的效率。
活跃队列与过期队列
每个 CPU 拥有一个 runqueue 结构,包含 active(活跃指针)、expired(过期指针)和 arrays 数组。
- 活跃队列:存放时间片未结束的进程,按优先级排序。
- 过期队列:存放时间片耗尽的进程。当活跃队列上的进程执行完毕,会被移至过期队列重新计算时间片。
随着进程执行,活跃队列进程减少,过期队列进程增多。当活跃队列为空时,直接交换 与 指针,原过期队列变为新活跃队列,继续调度。


