进程优先级
进程优先级
github地址
0. 前言
Linux
中的PCB既可以放在多叉树中,也可以放在双向链表中,又可以放在队列或哈希表中。进程的优先级与调度有关
在
Linux
世界中,进程的调度如同一场没有硝烟的竞赛。每一个进程都希望抢占到更多的CPU
时间片,以便更快地完成自己的使命。而谁能先一步被调度执行,背后正是“进程优先级”在发挥着关键作用。
在操作系统调度器的眼中,所有进程表面上被一视同仁,实则暗藏玄机:不同的优先级设定,决定了哪些进程能脱颖而出、率先登场。就像现实社会中有紧急任务、有普通事务,计算机系统也需要一套“轻重缓急”的调度准则,保证整体运行的效率与公平。
本篇博客将从最基础的概念入手,带你深入理解 Linux 中的进程优先级设计哲学、关键参数(如 nice 值与 PRI)、调度器背后的权衡机制,乃至于如何通过命令行手动干预进程优先级,帮助你在系统层面更高效地掌控进程行为!
1. 优先级是什么
优先级 VS 权限
- 优先级:对于资源的访问,优先级决定谁先访问,谁后访问。谈优先级时,代表已经有了访问资源的资格。
- 权限:权限代表对于某种资源,有没有资格访问。
2. 为什么要有进程优先级
CPU
只有一个,而进程有很多个。资源是有限的,进程之间是竞争关系。多个进程之间注定会竞争CPU
资源- 操作系统是公平的进程调度者,为了保证进程之间良性竞争
CPU
资源,必须给每个进程确认优先级
进程的饥饿问题:
- 如果进程的优先级或进程调度算法设置的不合理,会导致某个进程长时间得不到
CPU
资源,该进程的代码长时间无法得到推进,就引起了该进程的饥饿问题。- 饥饿问题在
windows
下的具体表现为:某个程序卡死了无法响应。
- 饥饿问题在
3. Linux中进程的优先级
1. 查看进程的优先级
命令: ps -l
或 ps -al
ps -l
只会显示在当前bash
终端下运行的进程ps -al
会显示在所有bash
终端下运行的进程
2. 关键信息
我们很容易注意到其中的几个重要信息,有下:
- UID : 代表执行者的身份
Linux
中是用数字来表示用户的,也就是UID
。ls -n
选项可以显示用户的UID
- PID : 代表这个进程的代号
- PPID :代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号
- PRI :代表这个进程可被执行的优先级,其值越小越早被执行
- NI:代表这个进程的
nice
值,表示进程优先级的修正数据 - 注意关键数据
PRI
和NI
- NI(nice)的存在表明
Linux
中进程的优先级是可以更改的,启动前启动中都可以更改。改的时候改的是NI
的值
- NI(nice)的存在表明
3. 进程优先级的范围和调整
范围:
我们知道优先级可以被调整!那我们可以大幅度的修改nice值,来大幅度提高我们进程的优先级,来达到让我们的进程几乎一直再被调度的目的吗?
Linux
中不可以!
Linux中的调度器是为了尽可能公平的调度每个进程,不想过多的让用户参与优先级的调整。
PRI and NI
- PRI:即进程的优先级,或者通俗点说就是程序被CPU执行的先后顺序,此值越小进程的优先级别越高
NI
:即nice
值,其表示进程可被执行的优先级的修正数值
Linux
中进程的优先级的算法为:PRI(new)=PRI(old)+nice
,PRI
值越小,该进程的优先级越高,越快被执行。
用户无法直接修改PRI
的值,但可以通过修改nice
值,间接修改PRI
的值
- 当
nice
值为负值的时候,该程序优先级值将变小,即其优先级会变高,越快被执行 - 所以,调整进程优先级,在Linux下,就是调整进程nice值
Linux中的调度器是为了尽可能公平的调度每个进程,不想过多的让用户参与优先级的调整。因此Linux
为nice
值设定了一个调整范围:
nice
其取值范围是负20至19,优先级一共40个等级。[-20, 19]
Ubuntu22.04
中进程的优先级初始为20,因此进程优先级的范围为0至39。[0, 39]
调整:
首先:所有进程的优先级,一般不要轻易更改!
命令:
nice:nice [-n NI值] 命令
。
- -n NI值:给命令赋予 NI 值,该值的范围为 -20~19
例如:nice
常用于在进程启动前设置其优先级
nice -n -5 service httpd start
:该命令将service httpd start
该httpd
进程的nice
值设置成-
5,再根据计算公式可以计算出相应的优先级。
renice: renice [优先级] PID
同 nice
命令恰恰相反,renice
命令可以在进程运行时修改其 NI
值,从而调整优先级。此命令中使用的是进程的 PID 号,因此常与 ps 等命令配合使用
使用top
命令更改进程的优先级:
- 步骤:
- 切换为root用户或使用
sudo top
命令提权输入top
命令回车运行 - 进入top页面后输入r
- 输入要修改优先级的进程的PID
- 再输入要更改的
nice
值。
可以看到以普通用户进行修改时,权限被拒绝了。
需要注意的是,
Linux
中进程的优先级的算法为:PRI(new)=PRI(old)+nice
。此处的
PRI(old)
是一个固定的值,Ubuntu22.04
中是20,CentOS 7
中是80。每次调整优先级时,只需要让固定值(Ubuntu22.04中是20,CentOS 7中是80)加上新的nice值,即可得到新的优先级。
4. 进程相关概念总结
竞争性
- 系统进程数目众多,而
CPU
资源以及其他硬件只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级
独立性
- 定义:多个进程在运行时拥有独立的地址空间和资源,独享各种资源,多进程运行期间互不干扰的特性
并行
- 定义:多个进程在多个CPU或物理核心上同时执行的现象,是物理层面的真正同步运行
并发
进程并发是指在同一时间段内,多个进程被操作系统调度并交替执行的能力
- 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程的代码都得以推进,称之为并发执行
由于CPU运行的速度极快,即使CPU多次切换,我们运行的多个进程也不会觉得卡顿
基于进程切换基于时间片轮转的调度算法实现并发:
- 时间段内的多任务处理
- 并发强调在一个时间段内(而非同一时刻)有多个进程处于运行状态,操作系统通过时间片轮转(Time Slicing)在进程间快速切换,使它们交替执行。
- 例如:单核CPU上同时运行浏览器和音乐播放器,用户感知二者“同时工作”,实际是CPU在毫秒级切换进程。
- 并发强调在一个时间段内(而非同一时刻)有多个进程处于运行状态,操作系统通过时间片轮转(Time Slicing)在进程间快速切换,使它们交替执行。
- 依赖CPU调度
Linux内核的进程调度器负责分配CPU时间片,根据优先级、状态等属性决定进程执行顺序,无需多核硬件支持。
0. 两个问题
1. 函数调用后,函数内定义的都是局部变量,return时也是局部变量,return时,为什么返回值会被外部拿到呢?
外部是通过寄存器拿到函数
return
的数据的,具体是哪个寄存器由编译器决定return ret
会被翻译成mov eax, 10
(假设ret的值为10)等类似的汇编语句,通过mov
指令将返回值存放在寄存器中
2. 计算机如何知道我们的进程当前执行到哪一行代码了?下一行该执行哪行代码?
- CPU内部有一个寄存器:指令指针寄存器(PC指针),也被称为程序计数器或eip寄存器。
- PC指针寄存器:记录当前进程正在执行指令的下一行指令的地址。
CPU
内部有很多寄存器,寄存器在CPU
中扮演什么角色呢?
寄存器是最靠近CPU的存储器,存取数据的速度极快。将数据放入寄存器中,主要目的就是为了提高存取效率。
为了提高进程的效率,进程的一些高频数据会被放入寄存器中。
保存在CPU寄存器内部的、进程相关的临时高频数据,被称为进程的上下文
暂时认为进程的上下文数据被保存到了
PCB
中即可。但实际上因为保存到PCB
中(内存级别)速度相较于太慢了,操作系统会有自己的硬件级别的做法。
1. 进程的上下文数据
定义:进程的上下文数据(Context Data)
是描述进程执行环境的完整状态信息集合,用于在进程被切换出CPU时保存其运行现场,确保恢复时能继续执行。
核心组成包括(按层次划分):
- 用户级上下文:用户空间的代码段、数据段、用户栈及共享内存区域。
- 寄存器上下文:
- 程序计数器(PC):存储下一条待执行指令地址。
- 栈指针(SP):指向当前栈顶位置。
- 状态寄存器(PSW/EFLAGS):记录CPU状态(如中断屏蔽位)。
- 通用寄存器(EAX、EBX等):存储临时计算数据。
- 系统级上下文:
- 进程控制块(PCB/
task_struct
):包含进程ID、优先级、资源使用等元数据。 - 内存管理信息:页全局目录(PGD)、页表项(PTE)等虚拟地址映射数据。
- 内核栈:内核态函数调用的栈空间。
- 进程控制块(PCB/
作用:上下文数据是进程暂停和恢复的“快照”,例如时间片耗尽时保存寄存器值,待重新调度时恢复现场。
2. 进程切换
定义:进程切换是操作系统在多任务环境下将CPU使用权从当前运行进程转移到另一待运行进程的过程。其本质是上下文数据的保存与恢复,以实现进程的并发执行。
进程再从CPU上离开时,要将自己的上下文数据保存好甚至带走。保存的目的,未来都是为了恢复
触发条件/发生情景:
- 时间片耗尽(分时调度)。
- 进程主动阻塞(如I/O请求)。
- 高优先级进程抢占。
- 进程结束运行。
发生进程切换时会有:
- 保存上下文
- 恢复上下文
3. Linux中进程切换的步骤
进程切换需在内核态完成,分为以下阶段:
- 保存当前进程上下文:
- 将CPU寄存器(PC、SP、通用寄存器等)保存到当前进程的PCB(如
task_struct->thread.cpu_context
)。 - 内核栈指针等状态同步更新至PCB。
- 将CPU寄存器(PC、SP、通用寄存器等)保存到当前进程的PCB(如
- 选择下一个进程:
- 调度器根据算法(如时间片轮转、优先级)从就绪队列选取目标进程。
- 地址空间切换:
- 更新页表基址寄存器(如ARM64的
TTBR0_EL1
),指向新进程的页全局目录(PGD)。 - 清空TLB或使用ASID(地址空间标识符)避免缓存冲突。
- 更新页表基址寄存器(如ARM64的
- 恢复新进程上下文:
- 从新进程的PCB中加载寄存器值(PC、SP等)到CPU。
- 切换内核栈,将执行流跳转到新进程被中断的指令地址(通过
ret
指令实现)。
- 更新运行状态:
- 新进程状态从就绪态(Ready)转为运行态(Running),旧进程可能进入阻塞或就绪队列。
4. 总结
- 上下文数据是进程的“执行快照”,包含用户/寄存器/系统级信息。
- 进程切换是CPU使用权的转移,依赖上下文保存与恢复机制。
- 切换步骤的核心是地址空间更新(页表切换)和硬件状态恢复(寄存器加载)。
这一机制使得单核CPU可通过快速切换(毫秒级)实现多进程并发执行的假象,而多核CPU可真正并行。
5. 结语
“优先级”不仅是操作系统中的一个参数,更是一种资源调配的智慧。
通过本文的学习,我们了解到在多任务操作系统中,优先级机制如何协调有限资源与无限任务之间的矛盾。无论是 nice
的细致调控,还是进程切换的幕后机制,都体现了 Linux
内核“公平而高效”的设计理念。
进程优先级并非洪水猛兽,也不是一成不变的设定,而是一个可以精细调整的工具。理解它、掌握它,便能更好地优化系统性能,排查卡顿问题,甚至打造属于你自己的系统调度策略。
以上就是本文的所有内容了,如果觉得文章对你有帮助,欢迎 点赞⭐收藏 支持!如有疑问或建议,请在评论区留言交流,我们一起进步
分享到此结束啦
一键三连,好运连连!
你的每一次互动,都是对作者最大的鼓励!
征程尚未结束,让我们在广阔的世界里继续前行!
🚀