一、什么是进程优先级
进程优先级,就是CPU 资源分配的先后顺序。优先级高的进程,拥有优先被 CPU 调度执行的权利。
在多任务 Linux 环境下,合理配置进程优先级,可以优化整机性能;我们还可以把低优先级后台任务绑定到指定 CPU 核心,保障交互式程序响应速度。
使用ps -l命令可以查看进程优先级相关字段:
cpp
ps -l
| 字段 | 含义 |
|---|---|
| UID | 进程执行者用户 ID |
| PID | 进程 ID,进程唯一标识 |
| PPID | 父进程 ID |
| PRI | 进程优先级,数值越小,优先级越高 |
| NI | nice 值,优先级修正数据 |
二、PRI 和 NI (nice 值)
1. 概念区分
- PRI(Priority):进程实际调度优先级,数值越小,进程越早被 CPU 执行,该值由内核动态计算维护。
- NI(Nice) :优先级修正值,它本身不是优先级,是用来修改 PRI 的修正量。
计算公式:
PRI(new) = PRI(old) + nice
注意:PRI (old) 不是修改后的 PRI,是内核默认基准优先级,一般默认 80,不是上一次修改后的 PRI 值。
2.nice 取值范围
nice 取值:-20 ~ 19,一共 40 个级别
nice=-20:修正幅度最大,进程优先级最高;只有 root 用户可以设置负数 nicenice=0:进程默认 nice 值nice=19:修正幅度最小,进程优先级最低
普通用户只能把 nice 调大(降低进程优先级),不能设置负数;root 用户可以设置全部范围。
3. 重要提醒
修改 nice 只是给调度器一个建议,不代表进程会独占 CPU,Linux 调度器依然会保证所有进程都有机会运行,不会出现进程永久饥饿。
三、修改进程优先级的几种方式
方式 1:top 交互式修改(运行中的进程)
- 输入
top进入 top 监控界面 - 按下
r,提示输入 PID - 输入目标进程 PID,回车
- 输入新的 nice 值,回车完成修改
- q 退出 top,使用
ps -l验证修改结果。
方式 2:nice 命令,启动程序的时候就指定优先级
nice 用于还没启动的程序,程序运行前设置 nice 值。
cpp
# 以nice=10启动程序,降低优先级
nice -n 10 ./test
# root设置高优先级
nice -n -10 ./test
方式 3:renice 命令,修改已经运行的进程
renice 针对已经在运行的进程,通过 PID 修改 nice 值。
cpp
# 将PID 1234的进程nice修改为5
renice 5 -p 1234
# root设置高优先级
renice -10 -p 1234
方式 4:系统调用,代码中修改优先级
头文件:<sys/time.h>、<sys/resource.h>
cpp
// 获取优先级
int getpriority(int which, int who);
// 设置优先级
int setpriority(int which, int who, int prio);
四、补充四个基础概念:竞争、独立、并行、并发
理解优先级,需要先理解进程四个核心属性:
- 竞争性 系统中进程数量很多,但 CPU 核心数有限。进程之间会竞争 CPU、内存等硬件资源,优先级就是用来规范竞争的规则,让重要进程优先获取资源。
- 独立性 多个进程同时运行,每个进程独享自己的资源(虚拟地址空间、文件描述符等);进程之间运行互不干扰,一个进程崩溃,一般不会直接影响其他进程。
- 并行 Parallel 多 CPU 核心,多个进程真正同时运行。每个 CPU 核心跑一个进程,同一时刻多个进程都在执行。
- 并发 Concurrent 单个 CPU 核心,通过时间片快速切换进程。一段时间内,多个进程交替向前推进;微观上 CPU 同一时刻只跑一个进程,宏观看起来多个程序 "同时运行"。
通俗比喻:
- 并行:多台咖啡机,多队人同时做咖啡;
- 并发:一台咖啡机,多队人轮流使用机器。
五、进程切换
CPU上下⽂切换:其实际含义是任务切换, 或者CPU寄存器切换。当多任务内核决定运⾏另外的任务 时, 它保存正在运⾏任务的当前状态, 也就是CPU寄存器中的全部内容。这些内容被保存在任务⾃⼰的堆栈中, ⼊栈⼯作完成后就把下⼀个将要运⾏的任务的当前状况从该任务的栈中重新装入CPU寄存器, 并开始下⼀个任务的运⾏, 这⼀过程就是context switch。

参考Linux内核0.11代码

📌 注意:
时间⽚:当代计算机都是分时操作系统,没有进程都有它合适的时间⽚(其实就是⼀个计数
器)。时间⽚到达,进程就被操作系统从CPU中剥离下来。
1.Linux2.6内核进程O(1)调度队列

2.⼀个CPU拥有⼀个runqueue
• 如果有多个CPU就要考虑进程个数的负载均衡问题3-4-2 优先级
• 普通优先级:100〜139(我们都是普通的优先级,想想nice值的取值范围,可与之对应!)
• 实时优先级:0〜99(不关⼼)
3. 活动队列
• 时间⽚还没有结束的所有进程都按照优先级放在该队列
• nr_active: 总共有多少个运⾏状态的进程
• queue140: ⼀个元素就是⼀个进程队列,相同优先级的进程按照FIFO规则进⾏排队调度,所以,
数组下标就是优先级!
• 从该结构中,选择⼀个最合适的进程,过程是怎么的呢?
- 从0下表开始遍历queue140
- 找到第⼀个⾮空队列,该队列必定为优先级最⾼的队列
- 拿到选中队列的第⼀个进程,开始运⾏,调度完成!
- 遍历queue140时间复杂度是常数!但还是太低效了!
• bitmap5:⼀共140个优先级,⼀共140个进程队列,为了提⾼查找⾮空队列的效率,就可以⽤
5*32个⽐特位表⽰队列是否为空,这样,便可以⼤ 提⾼查找效率!

4. 过期队列
• 过期队列和活动队列结构⼀模⼀样
• 过期队列上放置的进程,都是时间⽚耗尽的进程
• 当活动队列上的进程都被处理完毕之后,对过期队列的进程进⾏时间⽚重新计算
5.active指针和expired指针
• active指针永远指向活动队列
• expired指针永远指向过期队列
• 可是活动队列上的进程会越来越少,过期队列上的进程会越来越多,因为进程时间⽚到期时⼀直
都存在的。
• 没关系,在合适的时候,只要能够交换active指针和expired指针的内容,就相当于有具有了⼀批
新的活动进程!