跳到主要内容
极客日志极客日志
首页博客AI提示词GitHub精选代理工具
搜索
|注册
博客列表
C算法

Linux 进程优先级与 O(1) 调度算法解析

综述由AI生成Linux 内核通过 O(1) 调度算法高效管理进程优先级。文章解析了 PRI 与 NI 的关系及 nice 值调整方法,深入剖析了活跃队列与过期队列的切换机制,以及侵入式链表在任务结构中的应用。最后补充了竞争、独立、并行与并发的核心概念,帮助理解分时操作系统的调度逻辑。

不羁发布于 2026/3/21更新于 2026/4/293 浏览
Linux 进程优先级与 O(1) 调度算法解析

Linux 进程优先级与 O(1) 调度算法解析

一、进程优先级的概念

CPU 资源有限,无法让运行队列中的进程同时执行。因此,操作系统需要决定谁先获得时间片,这个先后顺序就是进程优先级。

二、查看优先级信息

使用 ps -l 命令可以查看系统中更详细的进程信息:

进程详细信息

其中与进程优先级相关的核心字段是 PRI 和 NI,它们也是存在于 task_struct 结构体中的两个整型成员变量。

  • PRI:代表进程可被执行的优先级,数值越小越优先。
  • NI:代表进程的 nice 值。

1. PRI 与 NI 的理解

PRI 直观反映程序被 CPU 执行的先后顺序。NI 则是进程优先级的修正数值。

两者的关系公式为:进程 PRI 值 = 80 + 进程 nice 值。 nice 值变小,PRI 随之变小,进程优先级变高;反之亦然。调整进程优先级,在 Linux 下本质上就是调整 nice 值。 nice 值的取值范围是 -20 到 19,共 40 个级别。对应的 PRI 取值范围则是 [60, 99]。

2. 修改 nice 值

我们可以跑一个循环程序进行演示,此时查看 PRI 和 NI 默认分别为 80 和 0。

修改前 PRI 与 NI 默认值

一种修改方式是通过 top 命令动态调整已存在进程的 nice 值:输入 top 回车,再按 r 键,输入进程 PID,最后输入新的 nice 值。

top 修改流程

这里我将 nice 值尝试修改为 20。退出 top 后再次查询:

修改后结果

可以看到 nice 值变成了 19,这是因为 20 超过了规定范围,最高只能修改到 19。此时 PRI 值也就变成了 19 + 80 = 99,符合预期。不过生产环境中,频繁调整 nice 值需谨慎。

三、进程调度切换

系统会给每个进程分配时间片,执行完毕后自动让出 CPU。CPU 寄存器由所有进程共享,每次保存一个进程的上下文数据。当轮转回来时,临时数据再转移回寄存器。这种机制被称为分时操作系统。

那么,系统是如何高效切换调度进程的呢?以 Linux 2.6 内核为例,其核心调度机制采用了 O(1) 算法。

1. list_head 与 prio_array 结构

在源码的 task_struct 定义中,有两个关键成员:

task_struct 相关成员

prio_array_t *array 指向当前进程所属的优先级数组指针,记录该进程是在活跃队列还是过期队列。

prio_array 结构

list_head 结构

struct list_head 是一个双向链表结构,run_list 是 CPU 运行队列的核心。

run_list 结构

分析 prio_array_t 结构:

prio_array 详细

根据优先级选择进程队列的过程,本质上是哈希映射。虽然 queue 中存放的是 list_head 类型节点,看似没有进程信息,但这里涉及一个关键概念:容器对象(container_of)。

假设有一个结构体变量 struct A obj,它内部包含成员 a。如果只知道 a 的地址,可以通过 &a - &((struct A*)0->a) 计算出 obj 的地址。原理是结构体成员的地址偏移量固定,通过成员地址减去偏移量即可反推结构体起始地址。

实际上,queue 中的每一个 list_head 就是一个进程的 task_struct 的成员变量 struct list_head run_list。利用上述方法,就能用 run_list 的地址算出 task_struct 的地址,进而获取进程的各种信息。这种将链表节点嵌入目标结构体内部的设计,被称为'侵入式链表',无需额外内存开销且灵活复用。

侵入式链表示意

选定进程的策略很简单:从下标 0 开始遍历 queue,找到第一个非空队列,依次调度其中的进程。由于 queue 大小固定为 140,这种算法的时间复杂度是 O(1)。为了进一步提升效率,prio_array 中还包含一个 bitmap 位图,用 140 个比特位表示 140 个进程队列是否为空,利用位运算大大提高查找非空队列的效率。

2. 活跃队列与过期队列

一个 CPU 拥有一个 runqueue 结构,即 CPU 的运行队列:

runqueue 结构

  • active:活跃指针,指向的 prio_array_t 称为活跃队列。
  • expired:过期指针,指向的 prio_array_t 称为过期队列。
  • arrays:数组中存放着这两个队列。

活跃队列中存放时间片未结束的进程,变量 nr_active 记录运行状态进程数。过期队列中存放时间片耗尽的进程。当活跃队列上的进程执行完,就放入过期队列并重新计算时间片。

随着进程不断被执行,活跃队列进程减少,过期队列进程增多。当活跃队列清空,直接交换 active 与 expired 指针,刚才的过期队列变为新活跃队列,继续执行。

这套设计非常巧妙,处理特殊情况如下:

  • 新进程:插入到过期队列,等下一轮再执行。
  • 修改优先级:本轮位置不变,等到插入过期队列时再计算新优先级位置。这也是引入 nice 中间值的原因之一。

若只有一套队列,进程时间片用完或修改优先级时需在同一队列内重算和调整,会导致遍历开销变大,逻辑混乱。有了这样一套完整的调度逻辑,查找最合适调度进程的时间复杂度即为常数,不随进程增多而增加成本,这就是 O(1) 调度算法。

四、补充概念

  • 竞争:系统进程众多而 CPU 资源较少,进程间存在竞争性。合理竞争就有了进程优先级。
  • 独立:多进程运行需独享资源,互不干扰。父子进程之间也有独立性。
  • 并行:多个进程在多个 CPU 下分别同时运行。
  • 并发:多个进程在一个 CPU 下采用切换方式,在一段时间内让多个进程都得以推进。

目录

  1. Linux 进程优先级与 O(1) 调度算法解析
  2. 一、进程优先级的概念
  3. 二、查看优先级信息
  4. 1. PRI 与 NI 的理解
  5. 2. 修改 nice 值
  6. 三、进程调度切换
  7. 1. listhead 与 prioarray 结构
  8. 2. 活跃队列与过期队列
  9. 四、补充概念
  • 💰 8折买阿里云服务器限时8折了解详情
  • GPT-5.5 超高智商模型1元抵1刀ChatGPT中转购买
  • 代充Chatgpt Plus/pro 帐号了解详情
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Stack-Chan 机器人快速入门与配置指南
  • Stable Diffusion 完整训练与推理流程详解(含伪代码)
  • MVEL 表达式编译与执行测试
  • GitHub 学生认证与 PyCharm 配置 Copilot 全流程指南
  • OpenClaw 本地部署与 Ollama 集成配置指南
  • nanobot 轻量级 AI Agent 框架实战:搭建 QQ 机器人与搜索扩展
  • ToDesk 内置 ToClaw AI:科技新闻日报自动化实战
  • Telegram 搜索机器人推荐:高效查找频道与文件资源
  • AI Agent 技能(Skills)设计与编写实战指南
  • Mac mini M4 本地部署 OpenClaw + Ollama 接入飞书机器人实战
  • Java 运算符基础与使用指南
  • DIY 无人机电源管理:升压降压电路设计
  • C++ 类与对象:封装特性的实现与实战应用
  • Java 初识面向对象:类、对象与封装核心详解
  • 使用 Ollama 本地部署 Llama 3.1 大模型指南
  • Spring 整合 Shiro 使用 Redis 缓存会话时报错排查与解决
  • LeetCode Hot 100 精选:C 语言实现与思路解析(1-21)
  • GitHub Copilot 配置避坑指南与最佳实践
  • 开源软件管理实战指南:从问题诊断到高效运维
  • Llama-Factory 增量训练与检查点恢复完整指南

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online