当前位置: 首页 > news >正文

系统核心解析:深入操作系统内部机制——进程管理与控制指南(三)【进程优先级/切换/调度】


♥♥♥~~~~~~欢迎光临知星小度博客空间~~~~~~♥♥♥

♥♥♥零星地变得优秀~也能拼凑出星河~♥♥♥

♥♥♥我们一起努力成为更好的自己~♥♥♥

♥♥♥如果这一篇博客对你有帮助~别忘了点赞分享哦~♥♥♥

♥♥♥如果有什么问题可以评论区留言或者私信我哦~♥♥♥

✨✨✨✨✨✨个人主页✨✨✨✨✨✨

        前面我们已经学习了进程的前置知识,今天这一篇博客我们继续来学习进程,准备好了吗~我们发车去探索进程的奥秘啦~🚗🚗🚗🚗🚗🚗

目录

进程优先级😜

基本概念😘

Linux 中的进程优先级😍

PRI 和 NI😜

优先级计算公式😄

查看进程优先级😀

调整进程优先级的方法😋

使用 nice 和 renice 命令🙃

使用 top 命令动态调整😁

使用系统调用 setpriority 和 getpriority👍

总结与对比🐷

进程的竞争性、独立性、并行与并发😊

进程切换(Context Switching)😀

什么是进程切换?😊

为什么会进程切换?❤

进程切换步骤😭

Linux O(1) 调度器😍

理论核心架构😜

调度过程 😜

队列交换 🙃

为什么叫O(1)算法?👌

总结🐷


进程优先级😜

基本概念😘

1. 什么是进程优先级?

    进程优先级(Priority)操作系统调度器用来决定哪个进程先获得 CPU 资源的一种机制优先级高的进程会优先被调度执行。注意优先级与“权限”不同优先级决定的是“谁先谁后”,而权限决定的是“能否访问”。

2. 为什么需要进程优先级?

    资源有限性:CPU 资源有限,而进程数量众多,需要通过优先级合理分配资源。

    系统性能优化:通过调整进程优先级,可以确保关键任务优先执行,提升系统整体性能。

Linux 中的进程优先级😍

PRI 和 NI😜

    PRI(Priority):进程的实际优先级,值越小优先级越高

    NI(Nice):用于调整 PRI 的修正值,用户可通过修改 NI 来间接影响 PRI。

优先级计算公式😄

PRInew​=PRIold​+nice

            nice 的取值范围为 -20 到 19,共 40 个级别【Linux 是一个分时操作系统,这一个范围也保证了一定的公平公正】,过度调高某个进程的优先级可能导致其他进程“饥饿”【其他进程无法得到调度和CPU资源】

            PRI 的范围一般为 60 到 99(Linux 默认范围),Nice 值的 40 个级别(-20 到 19)正好一对一地映射到优先级(PRI)的 40 个级别(60 到 99)上。 NI 是用户可见的、可调整的“输入”,而 PRI 是内核内部实际使用的“输出”值。

查看进程优先级😀

使用 ps -l ps -al 命令

    我们可以发现【ps -l】和【ps -al】查询到的进程是两种不同的结果~这也就是它们的区别所在,我们来看看下面的对比表格~

特性ps -lps -al
显示范围仅当前终端所有终端
用户范围仅当前用户所有用户
进程数量较少较多
终端显示仅当前终端 (pts/0)多个终端 (pts/0, pts/1)

        除此之外,我们还可以发现进程的 PRI 都为 80,NI 都为 0——这正是 Linux 进程调度默认行为的体现~

        在大多数 Linux 系统中,80 是用户进程启动时的默认基准优先级。这个值(具体数值可能因内核版本和发行版略有不同,但 80 非常常见)被设计为调度队列中的一个中间值。这意味着新创建的进程不会天然地拥有很高或很低的优先级,它们在竞争 CPU 时间时处于一个相对公平的起跑线上。

        0 是默认的 Nice 值。Nice 值是用户或系统用来对默认优先级进行微调的修正值。一个为 0 的 NI 值表示“不做任何调整”,所以进程的最终优先级 PRI 就等于默认的基准优先级 80 + 0 = 80

调整进程优先级的方法😋

使用 nicerenice 命令🙃

         nice 用于启动一个进程并指定其 nice 值renice 用于修改已运行进程的 nice 值~

非特权用户只能调低优先级(提高 nice 值),不能调高(降低 nice 值)~

# 启动一个进程(code)并设置 nice 值为 10
nice -n 10 ./code# 修改已运行进程(PID=4340)的 nice 值为 5
renice -n 15 -p 4340

注意事项:普通用户只能降低优先级(增加 NI 值),只有 root 用户才能提高优先级(设置负的 NI 值)。nice 用于启动新进程时设置,renice 用于修改已运行进程的优先级。

使用 top 命令动态调整😁

  1. 命令行输入top;

  2. r

  3. 输入进程 PID;

  4. 输入新的 nice 值。

  5. 按q退出

注意事项:这是交互式实时调整,同样受权限限制(普通用户不能设置负值)【与 renice 命令完全相同。在 top 界面中,如果你是普通用户,尝试输入负值会得到 “Operation not permitted” 的错误】修改仅对进程的当前运行实例有效,进程重启后恢复默认优先级。

使用系统调用 setprioritygetpriority👍

注意事项:需要在程序代码中调用,主要权限限制与命令行相同。必须包含完善的错误处理(如检查返回值),适合在应用程序中实现自适应的优先级管理。

总结与对比🐷

特性nice / renice 命令top 命令系统调用
使用场景命令行、脚本交互式实时监控与调整程序内部集成
控制对象新进程 / 已存在进程已存在进程自身或其他进程(编程控制)
灵活性交互式,中最高,可编程逻辑控制
权限要求普通用户只能降级普通用户只能降级普通用户只能降级
持久性进程运行期间有效进程运行期间有效进程运行期间有效
主要优势简单直接,易于脚本化实时直观,结合系统监控灵活自动化,嵌入程序逻辑

通用重要注意事项

  1. 权限是核心:记住 -20-1 【负值】的区间是 root 的“特权区”。

  2. 公平性:不要滥用高优先级,否则可能导致系统资源饥饿(Starvation),影响其他重要进程和系统整体稳定性。

  3. 非永久性:所有修改都只在进程生命周期内有效,进程重启后设置失效。

进程的竞争性、独立性、并行与并发😊

竞争性多个进程竞争有限的 CPU 资源,优先级用于解决竞争问题

独立性进程之间相互隔离,一个进程的崩溃不会直接影响其他进程

并行多个进程多个 CPU 核心上同时执行

并发多个进程一个 CPU 核心上通过时间片轮转交替执行,宏观上间隔时间很短看似同时运行

进程切换(Context Switching)😀

什么是进程切换?😊

        进程切换是指操作系统将当前正在运行的进程挂起,并恢复另一个进程执行的过程。这个过程也叫做 CPU 上下文切换(CPU Context Switching)

            那么什么是上下文呢?前面对于进程学习,我们简单介绍了一下上下文数据~   接下来我们来看看什么是CPU上下文?

  • 理论:CPU上下文指的是在任务(进程/线程)执行时,CPU寄存器程序计数器在任意时刻的状态。CPU内的寄存器只有一份,但是上下文可以有多份,分别对应不同的进程寄存器存储着进程的临时数据、地址、状态信息程序计数器(PC)存储着下一条要执行的指令地址

  • 比喻:这就像 一个厨师的工作台当前状态——哪些食材正在处理、刀放在哪、火开到多大、菜谱翻到了哪一页。这一切定义了“工作做到哪一步了”。

为什么会进程切换?❤

  • 理论:操作系统为了实现并发(Concurrency)让多个进程在一段时间内“看起来”同时运行。当一个进程的时间片用完、或需要等待I/O操作、或有更高优先级进程就绪时,就需要切换。

  • 比喻:餐厅经理(操作系统)为了保证公平性和效率,规定每个厨师一次只能在厨房工作一段时间(时间片)。时间到了就必须换人,让其他厨师也能用厨房,防止一个厨师霸占厨房。

进程切换步骤😭

  1. 保存上下文 (Save the Context):

    • 理论:将当前进程的CPU寄存器状态全部保存到它的内核栈(Kernel Stack) 中。

    • 比喻:厨师A被叫出厨房前,他必须迅速而准确地在自己的记事本(内核栈) 上记下所有工作状态:“洋葱切了一半,还剩3个,汤用中火炖了5分钟,盐放了2勺...”。

  2. 选择下一个进程 (Pick Next Process):

    • 理论:由操作系统的调度器(Scheduler) 从就绪队列中选择一个最合适的进程来运行。

    • 比喻:餐厅经理查看任务板(运行队列),根据优先级规则,决定接下来让哪位厨师(进程)进入厨房。

  3. 恢复上下文 (Restore the Context):

    • 理论:将下一个要运行进程的上下文信息,从其内核栈中加载到CPU的各个寄存器中。

    • 比喻:厨师B进入厨房。他并不关心上一位厨师做了什么。他拿出自己的记事本,按照上面的记录恢复工作现场:“哦,我上次鱼煎到一半,油温是七成热,下一步该翻面了...”。

  4. 跳转执行 (Jump and Execute):

    • 理论:将程序计数器(PC)设置为新进程的下一条指令地址,CPU开始执行新进程的代码。

    • 比喻:厨师B看了一眼记事本上记录的下一步骤,然后开始继续操作。

Linux O(1) 调度器😍

理论核心架构😜

运行队列 (Runqueue):

  • 理论:Linux为每个CPU核心都维护一个struct runqueue(运行队列)结构。这是调度的核心数据结构。

  • 比喻:这家餐厅的每个厨房(CPU核心) 都有自己独立的任务管理区(运行队列),这样多个厨房可以同时工作,互不干扰,避免了厨师们挤在一个任务板前争吵(避免了锁竞争)。

优先级数组 (Priority Arrays) - 活动队列与过期队列:

  • 理论:每个运行队列包含两个prio_array_t结构的数组:活动队列(active)过期队列(expired)

    • 活动队列 (active):存放所有时间片尚未耗尽的就绪进程。

    • 过期队列 (expired):存放所有时间片已经耗尽的就绪进程【新增进程/时间片到了的进程

  • 比喻:每个厨房的任务管理区都有两块巨大的白板

    • “正在叫号”板 (Active Board):上面挂着所有还有工作时间的厨师的订单。

    • “等待换班”板 (Expired Board):上面挂着所有工作时间已用完的厨师的订单。

队列结构 (Queue Structure):

  • 理论:每个prio_array_t包含:

    • queue[140]: 一个包含140个链表的数组。每个链表(queue[i])都是一个FIFO队列,存放着所有优先级为i的进程。下标即优先级,目前我们主要了解O(1)调度算法如何管理调度下标为【100-139】的普通进程【0-99】的实时进程的管理调度后面再继续了解~

    • bitmap[5]: 一个5*32=160位的位图(bitmap),每一位代表一个优先级队列是否为空(1为非空,0为空)。用于快速查找最高优先级的非空队列。

  • 比喻

    • “正在叫号”白板上有140个挂钩(queue[140],编号从0到139。编号越小,代表优先级越高(VIP挂钩)。

    • 每个挂钩上可以挂一串订单(进程链表),同一挂钩上的订单优先级相同,按先来后到的顺序排队。

    • 经理手边还有一个电子指示屏(bitmap,上面有140个小灯,每个灯对应一个挂钩。如果某个挂钩上有订单,对应的灯就会亮起。经理一眼扫过屏幕,就能立刻知道哪个编号最小的亮灯挂钩(即最高优先级的非空队列)。

更加形象可以看看下面这张图片:

调度过程 😜

  • 理论步骤

    1. 调度器查看active->bitmap,找到第一个被设置的位(即优先级最高的非空队列)。

    2. active->queue[i]中取出第一个进程。

    3. 将该进程投入运行。

    4. 当该进程的时间片用完,它会被从CPU上剥离。

    5. 调度器重新计算它的时间片和新优先级(可能根据其行为交互式还是计算型进行动态调整),然后将其放入expired->queue[k]中(k是计算出的新优先级)。

    6. 重复步骤1-5,直到active队列为空。

  • 比喻步骤

    1. 经理查看电子指示屏,找到编号最小的、灯还亮着的挂钩(比如101号)。

    2. 他走到101号挂钩前,取下最前面的那个订单,叫对应的厨师(比如厨师A)进厨房工作。

    3. 厨师A在厨房工作,直到经理喊“时间到!”。

    4. 厨师A出来,经理根据他刚才的表现(是一直在切菜(CPU密集型)还是经常等送食材(I/O密集型)),重新评估他的效率,并给他分配新的下次工作时间(重置时间片)。

    5. 然后经理把厨师A的订单挂到 “等待换班”板 的对应优先级的挂钩上(比如新优先级变成了105)。

    6. 经理继续从“正在叫号”板叫下一个厨师。

队列交换 🙃

  • 理论:当active队列完全为空时,调度器只需执行一个简单的指针交换操作swap(active, expired)。之后,原来的过期队列变成新的活动队列,而空的活动队列则成为新的过期队列

  • 比喻:当 “正在叫号”板 上的所有订单都被处理完,变得空空如也时。经理并不慌张,他做了一件非常聪明的事:直接把“正在叫号”和“等待换班”两块白板的标签互换!瞬间,满是订单的“等待换班”板变成了新的“正在叫号”板,而空的板子则成为了新的“等待换班”板。所有等待的厨师又都有了新的工作时间。

为什么叫O(1)算法?👌

  • 理论:无论系统中有多少进程,查找下一个要运行的进程的时间都是一个常数。因为它不遍历所有进程,而是通过查询固定大小的bitmap(常数时间)操作固定数量的队列(140个)来完成。

  • 比喻:无论餐厅有多少等待的厨师,经理决定下一个叫谁的时间都是一样的。他只需要看一眼固定大小的电子指示屏,而不是从头到尾数一遍所有厨师。

总结🐷

了解了这么多,我们可以看到Linux调度器的设计之美:

  • 效率:通过bitmap和双队列结构,将调度算法复杂度降为O(1)。

  • 公平:通过时间片轮转和优先级结合,保证所有进程都能得到执行,不会出现“饥饿”现象。

  • 智能:调度器会根据进程的过去行为(I/O密集还是CPU密集)动态调整其优先级,让交互式程序(如桌面点击)响应更快,体验更好。


♥♥♥本篇博客内容结束,期待与各位优秀程序员交流,有什么问题请私信♥♥♥

♥♥♥如果这一篇博客对你有帮助~别忘了点赞分享哦~♥♥♥

✨✨✨✨✨✨个人主页✨✨✨✨✨✨



文章转载自:

http://qdni6Dic.jcypk.cn
http://Epf7PB95.jcypk.cn
http://VenRFOMG.jcypk.cn
http://WPUUxMrp.jcypk.cn
http://3LAhPYY0.jcypk.cn
http://DfVHq0dJ.jcypk.cn
http://1fvYQl98.jcypk.cn
http://WMqxe7wD.jcypk.cn
http://labgi7Rc.jcypk.cn
http://VoASw914.jcypk.cn
http://J1wA5FlZ.jcypk.cn
http://IT559DNl.jcypk.cn
http://9aCHSseP.jcypk.cn
http://oCQhvc0G.jcypk.cn
http://nbHQh6Va.jcypk.cn
http://UnKPmCDh.jcypk.cn
http://eU4Rn6lr.jcypk.cn
http://RmDok1ak.jcypk.cn
http://tj3VulKX.jcypk.cn
http://gezoCSrm.jcypk.cn
http://pAWAiENp.jcypk.cn
http://mkE8Yfpa.jcypk.cn
http://OGHSl6ib.jcypk.cn
http://Dabp6PEK.jcypk.cn
http://jl3Mg70p.jcypk.cn
http://8LXTocF9.jcypk.cn
http://O0wEqGtF.jcypk.cn
http://K0SbZmqG.jcypk.cn
http://yO9cgql2.jcypk.cn
http://G4e77WEl.jcypk.cn
http://www.dtcms.com/a/381996.html

相关文章:

  • Roo Code:用自然语言编程的VS Code扩展
  • 第8.4节:awk的内置时间处理函数
  • leetcode算法刷题的第三十四天
  • 【技术博客分享】LLM推理过程中的不确定问题
  • Vue3基础知识-setup()、ref()和reactive()
  • 规则系统架构风格
  • 宋红康 JVM 笔记 Day17|垃圾回收器
  • vue表单弹窗最大化无法渲染复杂组件内容
  • 加餐加餐!烧烤斗破苍穹
  • SCSS 中的Mixins 和 Includes,%是什么意思
  • RFID基础了解 --- RC522
  • 第九篇 永磁同步电机控制-弱磁控制
  • 搭建langchain4j+SpringBoot的Ai项目
  • 一次 Linux 高负载 (Load) 异常问题排查实录
  • 扩散模型进化史
  • 学习Python是一个循序渐进的过程,结合系统学习、持续实践和项目驱动,
  • EKSPod 资源利用率配置修复:从占位符到完整资源分析系统
  • MySql基础:数据类型
  • 鸿蒙中的智能设备数据分析实战:从采集到建模的完整实现指南
  • Scikit-Learn 对糖尿病数据集(回归任务)进行全面分析
  • Scikit-learn 对加州房价数据集(回归任务)进行全面分析
  • Scintil在集成光子学技术方面筹集了5800万美元。
  • 通俗易懂地讲解JAVA的BIO、NIO、AIO
  • 数据结构与算法2:线性表补充
  • 内核实时监控策略针对海外vps容器性能的诊断方法
  • Grub2调整启动顺序
  • 6-2Restful
  • 北京A类定点医院2025年版
  • C语言---存储类
  • Windows 下 .venv 激活脚本深度定制:同时注入 PyTorch 调试日志与国内网络加速通道——从“能跑”到“好调”的完整工程化方案