上周三晚上我差点把一台线上数据库服务器搞到不可用。Top里翻了一圈,CPU占用最高的进程居然是我自己前一天启动的离线数据导出任务,它把订单服务的CPU时间片抢得干干净净。幸好那会儿我还记得进程优先级这个东西,一条renice把导出任务压到最低,业务接口才慢慢缓过来。说实话,如果当时连这个都不懂,就只能看着超时告警干瞪眼。
进程优先级,说白了就是内核在决定"CPU时间片先给谁"时用的那把尺子。很多人把它当成top命令里的一个数字,扫一眼就过去了,等到几个服务互相抢CPU、系统卡得不像话时,才想起来这玩意儿值得好好研究。这篇文章我想把优先级机制、查看方式、调整命令和踩坑经验一次讲透,给正在玩Linux服务器、做服务部署或者搞桌面环境的朋友一套可以直接用的思路。你不用成为内核专家,但掌握进程优先级,等于手里多了一个在资源紧张时快速取舍的工具。
1. 优先级机制:数字越小越优先的底层逻辑
1.1 nice值:越"谦让"反而数字越大
进程优先级最直观的体现就是nice值。Linux里nice值的范围是-20到19,默认是0。很多人第一次看到会懵:为什么数值越大反而越不优先?因为nice这个词的本意是"谦让",你对别人越nice,就越会把CPU让给别人用,数值自然就越高。
从内核角度看,普通进程用的是CFS调度器,它不会简单按"优先级数字"来决定谁跑谁不跑,而是根据nice值换算出一个权重,然后按权重比例分配CPU时间。这里有个我实测下来很好用的参考数据:nice值每差1,权重大约差1.25倍;差5个nice值,CPU分配比例大约能差3倍;差10个nice值,差不多就差10倍。所以你给一个后台任务设nice=19,跟默认的nice=0相比,它几乎只会在系统闲着的时候才分到CPU。
具体到内核内部,nice值会映射成不同的weight值。我整理了一个大致对应关系,方便你建立直觉:
| nice值 | 大致weight | 相对默认权重占比 |
|---|---|---|
| -20 | 88761 | 约86.7倍 |
| -10 | 110 | 约10.7% |
| -5 | 335 | 约32.7% |
| 0 | 1024 | 100% |
| 5 | 335 | 约32.7% |
| 10 | 110 | 约10.7% |
| 19 | 15 | 约1.5% |
注意权重关系并不是线性的,但"数值越小越优先"这个结论在任何版本上都成立。下次你看到top里一个进程NI列是19,就知道它在CPU竞争里基本属于"捡漏"的角色。
1.2 PR、RT优先级和调度类:别把两套体系搞混
除了nice值,你还会在top和ps里看到PR列和rtprio列,这俩是很多人最容易混淆的地方。
对于普通进程,top里显示的PR值通常是20+nice值。比如一个进程nice是0,PR就是20;nice是5,PR就是25。但一旦进程变成了实时进程,PR列会直接显示rt,它的优先级就是另一套体系——实时优先级,范围是0到99,数值越大越优先。
这两套体系是平行的:普通进程无论nice降到多低(-20),它依然属于SCHED_OTHER调度类;而实时进程跑在SCHED_FIFO或SCHED_RR调度类下,任何实时优先级高于0的进程,都会优先于所有普通进程获得CPU。换句话说,一个RT优先级为1的进程,也会排在一堆nice=-20的普通进程前面。这个设计是内核刻意为之的:实时进程要的是"必须在规定时间内得到调度"的硬保证,而不是跟普通进程一起排队抢时间片。
调度类方面,日常最常见的是TS(SCHED_OTHER),也就是默认调度类;还有BATCH(适合非交互的批处理任务)和IDLE(只在CPU完全空闲时运行)。实时调度类则是FIFO和RR。不同调度类的进程不能直接用同一个数值比较优先级,所以我建议你看到PR显示rt时,第一反应不是"它数值多大",而是"它要走实时调度这条独立通道"。
1.3 继承与边界:nice值会沿着进程树传下去
nice值有个容易被忽略的特性:子进程会继承父进程的nice值。你用nice -n 19启动一个脚本,脚本里再启动的所有子进程、孙子进程,全部都是19。这个继承是一棵完整的进程树,不是只在第一层生效。
同时要区分的是:renice改变的是进程自身的nice值,已经存在的子进程不会被跟着改掉。比如你有个进程树,父进程nice=0
