探秘 Linux 进程:优先级、调度与切换的精妙机制
🌟 快来参与讨论💬,点赞👍、收藏⭐、分享📤,共创活力社区。🌟
🚩用通俗易懂且不失专业性的文字,讲解计算机领域那些看似枯燥的知识点🚩
在 Linux 操作系统的复杂体系中,进程管理犹如一座精密运转的时钟,进程优先级、调度算法以及进程切换等关键机制协同工作,确保系统高效、稳定地运行。下面,让我们一同深入探索这些机制的奥秘。
目录
一、进程优先级:资源分配的关键指针 🎯
(一)优先级的本质 🤔
基本概念
- cpu资源分配的先后顺序,就是指进程的优先权(priority)。
进程优先级如同比赛的起跑顺序,决定进程获取 CPU 资源的先后。在 Linux 里,每个进程都有优先级数值,数值越小,越易优先获得 CPU 青睐,快速执行任务。就像百米赛跑,起跑靠前的选手更易领先 🏃♂️。
优先级 VS 权限
- 权限决定能否可执行,优先级是已经有权限,只是先后问题。
(二)优先级存在的意义 🌟
系统资源犹如稀缺的宝藏,而众多进程都渴望从中获取足够份额以完成自身任务(进程之间是有竞争性的)。
操作系统保证进程的良性竞争,依据进程的重要程度和紧急程度,有条不紊地分配资源。
以日常使用场景为例:
- 视频播放场景:当我们观看高清视频时,视频解码与播放进程需要大量的系统资源以保证画面的流畅性和实时性,因此被赋予较高优先级。这确保了视频能流畅播放,避免卡顿,为我们带来良好的观看体验。
- 后台更新场景:后台的自动更新进程,由于其对及时性要求相对较低,优先级也就相应调低。它会在系统资源相对充裕时才会执行,避免干扰前台重要任务,如视频播放、文档编辑等。
解决粉丝的问题:如何进行批量化注释?🤔
在命令模式下输入:
ctrl + v
通过键盘上的 H J K L (左下上右 )地调整
再按 shift + i ( I )
最后按 Esc
我们来看下面的进程
查看
PRI就是优先级的 priority,NI:nice
我们很容易注意到其中的几个重要信息,有下:
- UID : 代表执行者的身份
- PID : 代表这个进程的代号
- PPID :代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号
- PRI :代表这个进程可被执行的优先级,其值越小越早被执行
- NI :代表这个进程的nice值
(三)优先级的设置方法 🔧
PRI and NI
- PRI也还是比较好理解的,即进程的优先级,或者通俗点说就是程序被CPU执行的先后顺序,此值越小进程的优先级别越高
- 那NI呢?就是我们所要说的nice值了,其表示进程可被执行的优先级的修正数值
- PRI值越小越快被执行,那么加入nice值后,将会使得PRI变为:PRI(new)=PRI(old)+nice
- 这样,当nice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行
- 所以,调整进程优先级,在Linux下,就是调整进程nice值
- nice其取值范围是-20至19,一共40个级别。
在 Linux 系统中,调整进程优先级主要通过修改进程的 nice 值来实现。nice 值的取值范围为 -20 到 19 ,这一范围就像一个调节进程运行速度的刻度盘:
- 取值影响:当 nice 值为负数时,相当于给进程的 “速度” 加上了助推器,进程的优先级会相应提高,从而更易被 CPU 调度执行。例如,将 nice 值设为 -5 ,相比默认值,该进程会在资源竞争中更具优势。
- 查看命令:我们可以借助 ps -l 命令来查看进程的优先级相关信息。其中,PRI 代表进程的实际优先级,NI 表示进程的 nice 值,它们之间遵循 PRI (new)=PRI (old)+nice 的关系。通过这个命令,我们能清楚了解进程当前的优先级状态。
- 调整命令:若要提高某个进程的优先级,可以使用 “sudo renice -n [调整值] PID” 命令(其中 PID 为目标进程的标识符)。比如,若要将进程号为 1234 的进程 nice 值调整为 -3 ,则输入 “sudo renice -n -3 1234”。不过,需要注意的是,就像驾驶特殊车辆需要特定资质一样,只有超级用户才具备重新启动其他用户进程或提高其优先级的权限,普通用户一般只能降低自身进程的优先级,以此维护系统的安全性与稳定性。
在实际操作中,你有没有尝试过调整进程优先级呢🧐?
优先级调整:
top命令
输入 r
输入-30,不允许调整 (要在root下调整,并且不能过度调整,取值要适量)
二、Linux 内核调度算法:资源分配的智慧引擎 🧠
(一)双队列架构:进程有序流动的通道 🚶♂️🚶♀️
Linux 内核的 O (1) 调度算法构建了 running 队列(容纳 0 - 99 优先级进程)和 waiting 队列(容纳 100 - 139 优先级进程)这两个关键队列:
- running 队列:running 队列宛如一条高速通道,其中的进程犹如肩负紧急使命的 “先遣部队”。当系统面临硬件故障、网络中断等紧急情况时,这些高优先级进程能够迅速响应,优先获取 CPU 资源并执行任务,保障系统的核心功能稳定运行。例如,在系统检测到硬盘故障时,负责故障处理和数据保护的进程在 running 队列中优先执行,防止数据丢失。
- waiting 队列:waiting 队列则像是一个有序的 “候车大厅”。当进程因等待 I/O 操作完成、获取特定信号量等原因暂时无法继续执行时,便会进入该队列等待。比如,当一个进程需要从磁盘读取大量数据时,由于磁盘 I/O 速度相对较慢,它会进入 waiting 队列,直到数据准备好。一旦 running 队列出现空闲资源,waiting 队列中的进程就会按照一定顺序依次进入 running 队列,如同乘客有序登上列车,确保进程运行的有条不紊,有效避免了资源竞争带来的混乱与冲突,极大地提升了系统的整体运行效率。这种设计是不是很巧妙,像不像生活中的排队场景呢😃?
(二)位图定位机制:精准查找进程的导航系统 🔍
为了在庞大且动态变化的进程集合中快速定位到需要调度的进程,内核引入了位图技术。这一位图就如同一个超级精准的导航系统,仅需 5 个字节(40 个比特位),就能像绘制详细地图一样,精确标记出每个优先级上是否存在进程:
- 原理优势:在繁忙的系统运行过程中,进程如同散布在广阔区域内的无数目标,若采用传统的逐个查找方式,效率将极其低下。而位图定位机制则通过简洁高效的位运算,如同在导航系统中输入目的地坐标,瞬间就能锁定存在进程的优先级区域,为调度器提供了快速且准确的决策依据,大大缩短了调度器查找就绪进程的时间,显著提高了系统的响应速度。
- 状态指示:当位图中所有位都为 0 时,就清晰地表明对应的队列已无待处理进程,调度器可据此及时调整资源分配策略,转向处理其他任务。
(三)双指针切换策略:队列间的无缝衔接桥梁 🏗️
双指针机制在调度算法中扮演着至关重要的角色,它如同连接 running 队列和 waiting 队列的一座桥梁,确保了进程在两个队列之间的无缝切换:
- 切换过程:当 running 队列中的所有进程都完成任务,队列变为空时,双指针就像训练有素的信号员,迅速进行切换操作,将 waiting 队列中的进程顺畅地引入 running 队列,开启新的执行周期。这一过程犹如接力赛中的接力棒交接,精准而迅速,保证了系统的持续运行,有效避免了因队列切换不及时而导致的进程停滞或系统卡顿现象。
- 多任务场景应用:在多任务并行的复杂场景下,比如同时运行多个大型应用程序、进行数据处理和网络传输等任务时,双指针切换策略能够确保每个进程都能在合适的时机获得 CPU 资源,使系统始终保持高效、稳定的运行状态,为用户提供流畅的使用体验。例如,当我们同时打开视频编辑软件、浏览器和音乐播放器时,双指针机制协调各进程在队列间切换,保证每个软件都能正常运行,互不干扰。在你同时运行多个程序时,有没有感受到系统的这种高效切换呢😃?
三、进程关键概念解读:深入理解系统运行的微观视角 🔎
关于进程的总结
(一)竞争性:资源竞争的激烈战场 ⚔️
在 Linux 系统这个充满挑战的 “战场” 上,进程之间围绕着 CPU 时间片、内存空间、I/O 设备等有限资源展开着激烈的竞争:
- 竞争方式:每个进程都如同一名渴望胜利的战士,试图获取更多资源以完成自身任务。而优先级则如同战场上的指挥策略,根据进程的重要性、紧急程度以及资源需求特性,决定着每个进程在资源竞争中的先后顺序。
- 游戏场景示例:以游戏运行场景为例,游戏的图形渲染进程负责实时生成精美的游戏画面,对帧率和响应速度要求极高,因此在资源竞争中具有较高优先级,以确保玩家能够获得流畅、逼真的游戏体验。而后台的游戏数据自动保存进程,虽然也不可或缺,但对及时性要求相对较低,优先级也就相应降低,会在系统资源相对充裕时执行,避免影响游戏的实时运行。通过这种基于优先级的资源竞争机制,系统能够实现资源的优化配置,优先保障关键任务的执行,从而提升整体的运行效率和用户体验。
(二)独立性:进程的专属隔离空间 🏰
每个进程在 Linux 系统中都拥有属于自己的 “独立城堡”,即在各自独立的地址空间内拥有专属的资源,包括内存区域、文件描述符、寄存器状态等:
- 独立性体现:这种独立性使得进程之间相互隔离,互不干扰,就像一座座独立的城堡,各自有着自己的运行规则和资源体系。当一个进程遭遇错误或异常,如某个软件因代码漏洞而崩溃时,就如同城堡内部发生了局部故障,但这并不会对其他进程的正常运行造成影响,其他软件依然能够稳定地工作。
- 系统稳定性保障:这种进程间的独立性为系统的稳定性和安全性提供了坚实的保障,使得用户可以放心地同时运行多个不同类型的软件,不必担心某个软件的故障会引发连锁反应,导致整个系统的瘫痪。
进程的独立性是怎么做到的呢?我们后面在进程的地址空间会讲解 😃
(三)并行与并发:多任务处理的不同策略 🚀
- 并行:多个进程在多个CPU下分别,同时进行运行,这称之并行。在多 CPU 系统中,多个进程能够充分利用多个 CPU 核心的强大计算能力,同时在不同的核心上并行运行:
- 视频编辑示例:例如,在专业的视频编辑软件中,视频的解码、特效添加、编码等不同处理环节可以分别分配到不同的 CPU 核心上同步进行,大大缩短了视频处理的总时间,显著提高了工作效率。这就好比多个工匠同时专注于各自负责的工序,共同协作完成一件复杂的作品,每个工匠都能充分发挥自己的技能,加快作品的完成速度。
- 优势总结:并行处理充分发挥了多核处理器的性能优势,能够快速处理大规模、复杂的任务,适用于对计算速度要求极高的场景。
- 并发:多个进程在一个CPU下采用进程切换的方式,在一段时间内,让多个进程得以推进,称之并发。在单 CPU 系统中,虽然只有一个 CPU,但通过时间片轮转调度机制,系统能够让多个进程在一段时间内交替获得 CPU 时间片,从而实现多任务的并发执行:
- 日常使用示例:例如,当我们在电脑上同时打开浏览器浏览网页、音乐播放器播放音乐和文档编辑软件编辑文档时,虽然看起来这些任务似乎是同时进行的,但实际上 CPU 是在极短的时间内快速地在各个进程之间切换,使得我们在宏观上感觉它们是并行运行的。这就如同杂耍艺人同时抛接多个球,通过快速而准确的操作,让观众感觉所有球都在空中同时运动,实现了多任务的高效处理。
- 实现原理:并发通过合理分配 CPU 时间片,让多个进程看似同时运行,提高了 CPU 的利用率,适用于处理多个相对轻量级、对及时性有一定要求的任务。
四、Linux 内核进程切换:数据流转的幕后守护者 ⏱️
了解进程切换
(一)CPU 寄存器与进程数据交互:数据传输的高速通道 🚄
我们来思考俩个问题:
CPU 寄存器在进程运行过程中扮演着数据传输 “高速公路” 的关键角色,不同寄存器各司其职:
- 函数返回值传递:函数的返回值如同在这条高速公路上飞驰的重要货物,通过寄存器快速传递给外部调用者。例如,当一个函数执行完毕后,其返回值会被精准地存入特定的寄存器(如 eax 寄存器),然后如同快递被迅速送达目的地一样,传递给调用该函数的程序部分。
- 运行导航:程序计数器 pc 和栈帧寄存器 eip 则共同构成了进程运行的 “导航系统”。它们时刻紧密跟踪进程的执行轨迹,精确记录下当前进程正在执行的下一条指令的地址,确保程序流程能够按照预定的逻辑顺利跳转和执行。无论是顺序执行、条件分支还是循环迭代,都离不开它们的精准指引。
例如,在程序执行过程中,当遇到条件判断语句时,程序计数器会根据判断结果决定下一条指令的地址,从而引导程序走向不同的执行路径。- 数据存储:通用寄存器就像高速公路旁的便捷 “储物仓库”,为进程运算提供了临时的数据存储场所。在进程进行各种算术、逻辑运算时,通用寄存器能够快速存储和提供运算所需的数据,满足进程在运行过程中的各种计算需求。例如,在进行两个数的加法运算时,这两个数会先被加载到通用寄存器中,然后在寄存器中进行加法操作,最后将结果存储回寄存器或内存中。
- 栈结构维护:栈帧寄存器则精心维护着函数调用的 “栈” 结构,如同高速公路的调度中心,确保函数的调用与返回过程有条不紊地进行。它不仅准确记录函数局部变量和参数的存储位置,还负责管理函数调用栈的增长和收缩。例如,当一个函数被调用时,栈帧寄存器会为该函数分配一块栈空间,用于存储函数的参数、局部变量以及返回地址等信息。当函数执行完毕后,栈帧寄存器会释放这块栈空间,确保栈结构的正确恢复。
- 状态指示:状态寄存器则如同汽车仪表盘上的指示灯,实时反映 CPU 的当前运行状态。它包含了诸如进位、溢出、中断标志等重要信息,为系统级操作和异常处理提供了关键的状态依据。例如,当进行算术运算时,如果结果超出了寄存器能够表示的范围,状态寄存器中的溢出标志位会被置位,系统可以根据这个标志位进行相应的处理,如抛出异常或采取特殊的计算方法。
这些寄存器相互协作、紧密配合,共同构建了进程高效运行的硬件基础,确保数据在 CPU 与进程之间能够快速、准确地传输和处理,如同高速公路上的各种设施协同工作,保障了交通的顺畅与高效。
(二)进程切换的本质:数据的保存与恢复艺术 🎨
当进程需要暂时离开 CPU 时,就如同一位技艺精湛的艺术家暂时放下手中的工作,它会小心翼翼地将自己的重要 “创作素材”,即上下文数据,妥善保存到 PCB(进程控制块)中:
- 保存过程:这一过程就像将工作室里的重要工具、半成品和设计图纸等物品整理归类,放入一个安全的文件柜中,确保数据的完整性和安全性。PCB 就如同这个专门为进程打造的 “文件柜”,其中设有特定的结构体用于存储进程的上下文数据,包括 CPU 寄存器的值、程序计数器的位置、栈指针的状态等关键信息。
- 恢复过程:而当进程再次需要执行时,系统就像这位艺术家重新回到工作室,打开文件柜,从 PCB 中取出之前保存的上下文数据,如同重新拿起那些重要的工具和素材,恢复进程的运行状态,让进程能够从上次中断的地方无缝继续执行。
例如,在多线程数据库查询的场景中,当一个线程在等待磁盘 I/O 操作完成时被切换出去,此时它的上下文数据被保存到 PCB 中。一旦磁盘数据准备就绪,触发中断唤醒线程,系统会依据 PCB 中保存的上下文信息,迅速恢复线程的寄存器状态、程序计数器值和栈指针位置,使线程能够立即从等待点继续执行后续的查询任务,保证了数据库操作的连贯性和准确性,避免因进程切换导致的数据不一致或操作重复问题,有力地保障了系统的稳定可靠运行。就像艺术家能够根据之前保存的工作状态,迅速继续创作,进程也能凭借保存的上下文数据,顺利延续自己的工作。
希望大家通过这篇文章,对 Linux 进程有了更深入、更全面的认识,在探索 Linux 世界的道路上迈出更加坚实的步伐💪。
如果大家对 Linux 进程还有其他疑问或想法,欢迎随时交流讨论呀🤗!
原文地址:https://blog.csdn.net/2301_82213854/article/details/145269498
免责声明:本站文章内容转载自网络资源,如侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!