Linux 2.6 内核进程 O(1) 调度队列

56 阅读4分钟

进程优先级

1. 基本概念

CPU 资源分配的先后顺序,就是指进程的优先权。优先权高的进程有优先执行的权利。配置进程优先权对多任务环境的 Linux 很有用,可以改善系统性能。此外,还可以通过设置CPU亲和性,把进程运行到指定的CPU上,这样一来,把不重要的进程安排到某个CPU,可以改善系统整体性能。

2. 查看系统进程

Linux 系统下可以用 ps -al 命令输出以下内容:

屏幕截图 2025-07-29 224755.jpg

  • 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 值
  • nice
  • renice

5. 进程切换

进程发生上下文切换的时候:

  • 内核栈:在上下文切换时,用于保存和恢复寄存器状态。
  • 用户堆栈:在上下文切换时保持不变,用于保存用户程序的函数调用栈、局部变量、返回地址和临时数据。

6. Linux 2.6 内核进程 O(1) 调度队列

O(1) 调度器使用了双向链表和位图的组合来实现高效的进程调度。

屏幕截图 2025-07-29 230157.png

运行队列(runqueue)组成

  • 元数据:包含锁(lock)、运行进程数(nr_running)、CPU 负载因子(cpu_load)等,用于调度控制和状态统计。

  • 双队列设计

    • 活跃队列(array[0] :存储时间片未耗尽的进程,通过 nr_active(活跃进程数)、bitmap[5](位掩码,快速标记非空优先级队列)、queue[140](140 个优先级链表,同优先级进程按 FIFO 排列)管理。
    • 过期队列(array[1] :存储时间片耗尽的进程,结构与活跃队列相同,通过 activeexpired 指针轮换(活跃队列为空时交换,重新分配时间片,避免进程饥饿)。

优先级与队列管理

  • 优先级范围0~1390~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优先级调整,但不会立即影响当前的时间片。进程会继续运行当前的时间片,直到时间片耗尽。当进程的时间片耗尽时,它会被移到过期队列中。在过期队列中,进程会根据新的优先级等待下一次队列交换。