新闻详情

新闻详情

首页 / 资讯中心 / 详情

深入理解Linux O(1)调度算法:进程优先级、双队列与性能调优

发布时间:2026/9/26 15:05:10来源:尧图网络
深入理解Linux O(1)调度算法:进程优先级、双队列与性能调优
有段时间只要服务器 load average 一高我就习惯性先重启机器。直到一次线上业务进程把 CPU 占满监控脚本迟迟跑不动我才意识到如果不理解 Linux 到底按什么规则把 CPU 分给进程排查这类问题就只能靠猜。这篇文章把进程优先级和调度切换中最经典的 O(1) 算法拆开讲清楚包括 nice 值怎么映射、active 和 expired 双队列为什么快、生产环境里到底能用哪些命令调优。适合三类读者经常跟 Linux 打交道、需要排查系统响应问题的运维准备内核或系统设计面试的开发者以及纯粹想搞懂 top 输出里 PR、NI 含义的新手。1. 先看清调度器在 Linux 里承担什么工作1.1 一个真实场景CPU 占满不等于系统死机我之前在测试环境遇到过一台 4 核的虚拟机某个数据导入脚本把四个核全部跑满系统 load average 到了 8 以上。当时第一反应是“完了卡死了”。但奇怪的是我敲top依然有响应ssh也能连上去只是明显感觉到其他操作变慢了。这里的关键在于 Linux 不是“一个进程跑完再跑下一个”而是把 CPU 时间切分成很小的时间片让多个进程轮流使用。即使某个脚本把 CPU 占满调度器也会强制把它踢下来把时间让给其他进程。所谓的“卡”往往不是系统真死而是调度器留给交互进程的时间太少或者某个关键进程被饿得太厉害。理解这一点就能明白为什么要研究优先级和调度算法调度器决定了谁先跑、谁后跑、谁一次能跑多久。优先级数值是“谁先谁后”的根据时间片是“一次跑多久”的根据O(1) 算法则是“怎么快速从一堆进程里挑出下一个”的根据。1.2 调度器的工作内容选进程换上下文从内核角度看调度器要回答三个问题哪些进程是“可以被调度”的这些进程里谁最应该被选中选中之后怎么把上一个进程的现场保存好再恢复新进程的现场第一个问题涉及进程状态。处于TASK_RUNNING状态的进程会进入运行队列等待分配 CPU处于睡眠状态的进程不在运行队列里自然也不会被调度。第二个问题就是优先级要管的。第三个问题叫上下文切换涉及寄存器、程序计数器、内核栈等内容的保存与恢复代价不低所以调度算法要尽量减少无意义的切换。你可以把运行队列想象成食堂窗口前排队的人调度器是打饭阿姨。优先级决定谁排前面时间片决定每个人打饭窗口期有多长O(1) 的意义则是队伍里哪怕有一万人阿姨也能立刻知道下一个该招呼谁而不是从队头到队尾数一遍。2. 进程优先级数值越小真的越优先吗2.1 nice 值、内核优先级和 top 显示值要分开看很多新手第一次看top会被 PR 和 NI 两列搞晕。先说结论在 Linux 内部优先级数值越小越优先但在用户态工具里不同工具对“优先级”的展示方式不一样不能拿一个数到处套。Linux 内核把进程优先级分成两大体系实时进程优先级范围 0~99数值越小越优先。普通进程优先级范围 100~139数值越小越优先。普通进程的优先级实际上和 nice 值挂钩。nice 值的范围是 -20~19默认 0。以 2.6 早期 O(1) 调度器的实现为例普通进程静态优先级大致等于120 nice所以 nice 为 -20 时对应内核优先级 100nice 为 0 时对应 120nice 为 19 时对应 139。每个内核版本的具体映射公式可能略有差异但这个单调关系是一致的nice 值越小内核对应的优先级数值越小进程越优先。至于top里的 PR 列它通常展示的是“用户友好的映射值”。普通进程的 PR 往往等于20 NI所以 nice 为 0 的进程你会看到 PR 20nice 为 -20 的进程会看到 PR 0。这里同样遵守数值越小越优先。实时进程在top里一般显示为rt或类似标识不能直接和普通进程的 PR 数字比较。下面这个表可以帮你快速对照名称范围说明nice 值-20 ~ 19用户态调整数值越小越优先内核实时优先级0 ~ 99值越小越优先配 SCHED_FIFO / SCHED_RR内核普通优先级100 ~ 139值越小越优先对应 nice 值top 中的 PR普通进程约 0~39常见映射为 20 NI数值越小越优先top 中的 NI-20 ~ 19直接显示 nice 值2.2 动态优先级给交互式进程的一点“补偿”O(1) 调度器并不只是用静态优先级来排队的它还会引入“动态优先级”的概念。思路很简单如果一个进程经常睡眠说明它大概率在等待 I/O比如键盘输入、网络数据包、磁盘读写这类进程对响应速度很敏感。如果总是让 CPU 密集型的进程占着位置交互式程序就会卡到没法用。调度器会统计进程的平均睡眠时间根据睡眠情况给普通进程计算一个奖励值bonus并在调度时使用动态优先级。具体表现就是交互式进程的动态优先级会比静态优先级更高数值更小从而更容易被选中而长期占用 CPU 的进程动态优先级会被压低数值更大避免它垄断处理器。不同内核版本对睡眠时间的统计和奖励幅度不完全一样但机制是稳定的。这个“奖励”不是随便设计的。试想一下你在终端里敲一个grep如果它要等 100 毫秒才被调度你会立刻感觉到敲慢如果调度器能把这类进程往前排系统“手感”就会好很多。这也是为什么 Linux 在桌面和服务器领域都能有不错表现的原因之一调度器会主动照顾交互体验。3. 理解 O(1) 调度算法为什么它能做到“与人多少无关”3.1 从 O(n) 到 O(1)老调度器到底慢在哪在 Linux 2.4 及更早的内核里调度器每次选下一个进程时往往需要遍历运行队列里的全部进程逐个比较优先级才能选出最小优先级那个。这种方式的时间复杂度是 O(n)n 是运行队列里的进程数。系统里进程少还看不出来一旦跑了几百上千个进程每次调度都要扫描一遍开销就很可观。调度器本身会被频繁调用每个时间片结束会触发、进程睡眠和唤醒会触发、中断返回也可能触发。如果一次调度的开销是 O(n)进程数量越多系统花在“决定谁该跑”上的时间就越多真正执行任务的时间反而变少。这就是旧内核在高负载下表现吃力的原因之一。O(1) 要解决的核心问题就是这个无论系统里有多少个进程调度器挑选下一个进程的时间都应该是常数级而不是跟着进程数量线性增长。3.2 active 与 expired两个优先级数组组成的“双队列”O(1) 调度器在每个 CPU 上维护了两套运行队列一套叫 active一套叫 expired。每套队列内部并不是一个普通链表而是 140 个链表头组成的数组分别对应 140 个优先级级别。也就是说优先级 0 的进程挂在第 0 个链表上优先级 1 的挂在第 1 个链表上依此类推。调度时调度器从 active 队列里找到当前最高优先级的非空链表然后取出链表头部的进程去执行。这个进程不会立刻被丢出队列它会一直待在 active 队列里只是获得了一个时间片。当它的时间片用完如果还没有执行完就会被移动到 expired 队列并且按它的优先级重新计算下一次的时间片。当 active 队列里所有进程的时间片都用完也就是 active 队列完全空了之后调度器会直接交换 active 和 expired 两个指针。原来的 expired 变成新的 active原来的 active 变成新的 expired。这个交换不需要移动任何进程数据只是换一下指针代价是常数级。顺序大致可以这样看从 active 选出最高优先级进程。调度该进程执行一个时间片。时间片用完进程若未完成放入 expired。active 空了交换两个队列指针继续下一轮。这样的结构保证了每次找进程时只需要看 active 队列里最高优先级的那个链表而不用关心 expired 队列当前有什么内容。3.3 位图加速从 140 个链表里立刻找到优先级最高的那个active 队列里虽然有 140 个链表但调度器并不能把链表头位置固定死了。它需要一个办法快速知道“当前哪些优先级级别上有进程”。这个办法就是位图。O(1) 调度器用 140 个 bit 来记录 active 队列的状态每一位对应一个优先级。如果该优先级上有进程对应位就是 1否则是 0。每往某个优先级链表里加入进程时就把对应位置成 1链表变空时再把它清成 0。选择下一个进程时调度器只需要找位图里最高位置的那个 1。在 x86 上可以用bsf这样的指令一步找到在 ARM 上也有对应的位扫描指令所以不管共有 100 个进程还是 10000 个进程找下一个进程的时间基本是固定的。这也是“O(1)”这个名字的真正含义它不表示调度器只执行一条指令而是说调度开销不会随着进程数增长而增长。这种“数组 位图”的思路其实很像停车场找空位如果用一个“空位指示牌”标记哪个车道有空位管理员一眼就能看见而不是逐辆数车。3.4 时间片用完不是结束而是排队去下一轮O(1) 调度器里时间片和优先级是绑定的。优先级越高一次能获得的时间片通常越长优先级越低时间片越短。这样设计是为了让高优先级进程少被切换低优先级进程即便被选上了也只能占很短的时间避免浪费在不重要的任务上。需要注意的是进程不会因为时间片用完就被“饿死”。它从 active 挪到 expired等 active 队列清空后经过队列指针交换又会回到 active 参与下一轮调度。这个过程会保证所有普通进程都能得到 CPU只是高优先级进程跑得更多、更快。说白了这不是“谁抢到谁就一直跑”而是一种有次序的轮流制优先级决定轮到的频率和时长。4. 实操查看优先级和调整优先级的常用命令4.1 用 ps 和 top 看清进程当前优先级排查问题时我一般先看全量进程再定位目标进程。最实用的命令ps -eo pid,ni,pri,comm --sort-ni | head -20这个命令会按照 nice 值从大到小排列也就是越不优先的进程越靠前方便你揪出那些“偷偷调高了 nice 值、优先级很低”的任务。如果已经知道 pid直接用top -p 1234可以单独观察。重点关注PR和NI两列。NI是 nice 值PR是工具展示的优先级。调整测试时这两列会实时变化。4.2 nice 和 renice给进程“排队”的正确姿势启动一个新进程时设置优先级用nicenice -n -5 ./my_job这里的 -5 表示把 nice 值设为 -5也就是比默认值更优先。注意命令写法里-n -5是两个参数-n是指定 nice 值后面的-5是值本身。新手最容易在这里踩坑写成nice -5在部分系统上也能被识别但可读性差不建议依赖这种写法。给已经运行的进程调整优先级用renicerenice -n -10 -p 1234意思是把 pid 为 1234 的进程 nice 值调整为 -10。这个操作必须清楚一点普通用户只能把 nice 值调大也就是让进程变得更不优先只有 root 用户才能把 nice 值调小让进程变得更优先。这是内核的权限控制目的是防止普通用户通过把优先级调到极高来霸占 CPU。我的建议是线上环境调整优先级前先做三件事确认 pid 没错、确认当前平均负载情况、确认你确实知道这个进程是干嘛的。我曾经见人把数据库进程renice -n -20后又把同一台机器上的备份任务给卡得动弹不得最后只能匆忙改回来。4.3 实时进程与 chrt小心驶得万年船普通进程的 nice 值只影响普通调度策略下的优先级。如果你想让某个进程使用实时调度策略那就需要chrt。chrt -f -p 80 1234这条命令把 pid 1234 设置为SCHED_FIFO实时进程优先级 80。SCHED_FIFO的意思是只要这个进程不主动让出 CPU 或阻塞它就会一直占着 CPU直到它自己完事。它还不需要经过 active/expired 那套普通进程的轮转流程。这种“高优先级实时进程”在生产环境里极其危险。一旦你给某个进程设置了很高的实时优先级而它又是一个 CPU 密集的死循环那么系统里其他进程包括内核的关键线程都可能拿不到 CPU。表现出来的症状就是系统负载不高但机器突然“假死”ping 都能通ssh 却半天没反应。所以chrt这类命令我只建议在明确可控的场景里用比如通过它管理系统中的实时音频任务、特定硬件控制程序。就算要用优先级也不要一下拉到 99从低到高一点点试并且设置好超时和退出机制。5. 排障经验与常见误区5.1 把优先级调得很高系统反而更卡了我在实践中见过最典型的问题就是有人为了让一个“紧急任务”跑得更快直接把它设成高实时优先级结果整台机器变得比之前还要卡。原因很简单实时优先级高的进程会抢占普通进程如果它一直不放弃 CPU连负责网络收包、磁盘刷盘的内核线程都排不上队系统整体性能反而崩掉。这就好比你给一个快递员配了“永远插队权”结果他每次都拖着一大车货堵在路口所有人都走不动。调度器设计的目标是分时共享不是让某个进程垄断。遇到这种情况先恢复现场chrt -o -p 0 1234-o表示切回普通调度策略优先级 0 只是普通策略下的占位参数。也可以直接renice -n 0 -p 1234。改完之后观察一分钟不要急着做其他调整。5.2 进程没反应先别急着调优先级renice不是万能药。系统响应慢原因可能是一堆磁盘 I/O 排队、内存 swap、数据库锁、网络带宽瓶颈、代码本身有死循环。优先级只能改变 CPU 调度顺序解决不了这些问题。我之前帮人看一台卡顿的服务器进程满天飞大家第一个想到的就是“调优先级”。结果最后发现是磁盘故障导致大量 I/O 等待进程都堵在 D 状态不可中断睡眠优先级根本不影响这种状态。排查顺序应该永远是“先看负载来源再看瓶颈类型最后才考虑要不要动优先级”。5.3 常见问题速查表症状可能原因处理建议某进程 CPU 占满其他进程卡顿进程被设置了实时调度策略且优先级较高用 chrt 切回普通调度策略并观察调大某个进程 nice 值后没效果服务器多核空闲进程本就不缺 CPU配合 taskset 绑核或降低业务并发普通用户 renice 调低优先级失败权限不足只能调大 nice 值联系管理员操作或通过 systemd 配置systemd 服务想启动时带优先级服务启动入口没带 nice 设置在 service 文件里配置 LimitNICE、Nice 等top 里 PR 数值和内核优先级对不上不同工具的显示映射不同属正常现象以 NI 列和内核文档为准理解如果你用 systemd 管理服务可以在 service 文件里加Nice-5 LimitNICE-5然后systemctl daemon-reload再重启服务。这种方式比在脚本里renice更可控也更容易追查。5.4 O(1) 已经被 CFS 替代为什么还要学它Linux 2.6.23 之后主调度器换成了 CFS完全公平调度器。CFS 不再使用固定优先级数组和时间片而是用红黑树维护进程的虚拟运行时间目标是让每个进程获得公平的 CPU 比例。O(1) 相当于退出了历史舞台。那为什么还要专门去解 O(1)两个原因。第一面试经常问因为它代表了一次经典设计演进从线性扫描到数组加分桶从“能在更多进程下工作”到“在任意规模下开销稳定”。第二它留下的很多思路还在影响现在的系统比如位图加速找最快空闲 CPU、按优先级分桶排队等思想在 Linux 的其他子系统里仍然能看到变体。理解了 O(1)再去看 CFS 的虚拟时间和权重分配会轻松很多。最后留一点自己的习惯在线上碰见 CPU 被某个业务进程占满不要第一反应就renice -n -20先看它是不是正在做该做的事如果只是短时间冲高吃几个时间片也就过去了。真要长期跑批处理我更倾向于用 systemd 或 cgroup 去限制资源而不是简单改一个 nice 值。调度器只是 Linux 众多子系统里很小的一块但把这里想通之后再去看top、htop、perf的输出会明显觉得底层的逻辑清晰了很多。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

TypeScript 函数返回类型推断(Type from Func Return)实战指南:从隐式推断到 ReturnType 提取 2026/9/26 15:47:33

TypeScript 函数返回类型推断(Type from Func Return)实战指南:从隐式推断到 ReturnType 提取

文档教程 【免费下载链接】typescript-book The Concise TypeScript Book: A Concise Guide to Effective Development in TypeScript. Free and Open Source. 项目地址: https://gitcode.com/gh_mirrors/typ/typescript-book 点击查看 免费下载 导读 本文基于 Th…

阅读更多 →
Agent 智能体开发实战 · 第一课:Tool Use —— 让大模型自动干活(TaoToken 统一 Key 配置版) 2026/9/26 15:47:33

Agent 智能体开发实战 · 第一课:Tool Use —— 让大模型自动干活(TaoToken 统一 Key 配置版)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
Atlas 300V 24G推理卡部署YOLO:从环境配置到性能调优全解析 2026/9/26 15:47:33

Atlas 300V 24G推理卡部署YOLO:从环境配置到性能调优全解析

作为一个在AI推理落地领域折腾了十来年的老工程师,最近被问得最多的两个问题恰好都和Atlas有关:一是"atlas 300v 24g 是运算加速卡吗",二是"atlas部署yolo到底怎么搞"。这两个问题看似一问一答,实际上背后牵出…

阅读更多 →
[个人笔记] WSL 完整使用指南及 Claude Code 配置记录:TaoToken 统一 Key 接入 settings.json 骨架 2026/9/26 15:47:33

[个人笔记] WSL 完整使用指南及 Claude Code 配置记录:TaoToken 统一 Key 接入 settings.json 骨架

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
PC/Console 游戏开发引擎选型与平台适配指南:Unity、Godot、Unreal 的决策、优化与工程实践 2026/9/26 15:47:26

PC/Console 游戏开发引擎选型与平台适配指南:Unity、Godot、Unreal 的决策、优化与工程实践

前端开发工具 【免费下载链接】dillinger The last Markdown editor, ever. 项目地址: https://gitcode.com/gh_mirrors/di/dillinger 点击查看 免费下载 本文以本仓库 .agent/skills/game-development/pc-games/SKILL.md 技能文档为核心骨架,系统讲解 …

阅读更多 →
Atlas 300V 24G部署YOLO实战:从PyTorch到OM模型转换全流程 2026/9/26 15:47:20

Atlas 300V 24G部署YOLO实战:从PyTorch到OM模型转换全流程

如果你最近在搞 AI 落地,十有八九会看到 atlas 这个词被反复提起。有人问 atlas 300v 24g 是不是运算加速卡,有人问 atlas 部署 yolo 到底行不行,我今天把这两个问题一起聊透。基于我自己在一台 x86 服务器上从零开始部署 YOLO 到 Atlas 300V…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉