进程优先级(Process Priority)

发布于:2025-09-03 ⋅ 阅读:(16) ⋅ 点赞:(0)

在这里插入图片描述


在这里插入图片描述


🎁个人主页:工藤新一¹

🔍系列专栏:C++面向对象(类和对象篇)

🌟心中的天空之城,终会照亮我前方的路

🎉欢迎大家点赞👍评论📝收藏⭐文章


进程优先级(Process Priority)

一、回顾:进程状态

  • 1. 状态是变量:进程状态在代码上是一个可修改的整型变量(task_struct->state),不同的值代表不同的状态宏

  • 2. 状态决定队列:这个状态值直接决定了进程应该位于哪个队列(运行队列 or 各种等待队列)。进程在哪里,就决定了它的行为

  • 3. 队列决定命运:

    • 在运行队列 -> 有资格被CPU调度 -> 表现为“运行”或“就绪”
    • 在等待队列 -> 不会被CPU调度 -> 表现为“阻塞”、“睡眠”、“卡住”
    • 被交换到磁盘 -> 既是阻塞,又不在内存 -> 表现为“挂起

在这里插入图片描述


a. 本质:task_struct 中的状态标志(state 字段)

  • 这是状态的“源代码视角”或“静态视角”。在 Linux 内核中,每个进程/线程都是一个 task_struct 结构体(称为进程描述符)。其中有一个关键的成员变量,比如 volatile long state;,它是一个整数。
  • 这个整数的值不是任意的,它由内核预先定义好的一系列宏来决定,例如:
    • TASK_RUNNING: 可运行状态。
    • TASK_INTERRUPTIBLE: 可中断的睡眠状态(一种阻塞)。
    • TASK_UNINTERRUPTIBLE: 不可中断的睡眠状态(另一种阻塞,通常发生在等待硬件I/O时)。
    • __TASK_STOPPED: 暂停状态(如收到 SIGSTOP 信号)。
    • TASK_DEAD / EXIT_ZOMBIE: 退出状态。
  • 您的结论完全正确:修改这个 state 变量的值,就改变了内核对这个进程状态的“官方认定”。

b. 行为:队列决定调度(运行 vs. 阻塞)

  • 这是状态的“内核调度视角”或“动态视角”。内核的调度器(Scheduler)不关心 state 这个数字本身,它只关心队列。调度器的核心工作就是从运行队列(Runqueue) 里选择一个最合适的进程来运行。
  • 运行(RUNNING):当一个进程的 stateTASK_RUNNING 时,它有资格被调度。此时,它一定存在于某个CPU的运行队列中(或者正在某个CPU上执行)。调度器只从这个队列里选人。
  • 阻塞/睡眠(BLOCKED/SLEEPING):当一个进程需要等待某个事件(如 scanf 等待键盘输入、read 等待磁盘数据、sleep 等待超时)时,内核会:
    1. 将其 state 设置为 TASK_INTERRUPTIBLETASK_UNINTERRUPTIBLE
    2. 将其从运行队列中移除。
    3. 将其加入到另一个等待队列(Wait Queue) 中,这个等待队列通常与它正在等待的资源(如键盘、硬盘的某个块)相关联。
  • 此时,因为这个进程已经不在运行队列里了,调度器根本看不到它,所以它绝对没有机会获得CPU,这就是它“卡住了”的原因。

c. 状态转换的完整流程

让我们用 scanf 的例子来串联这两个视角:

  1. 进程运行:进程在CPU上执行,state = TASK_RUNNING,位于运行队列。
  2. 调用 scanf:进程执行到 scanf 系统调用,代码进入内核态。
  3. 内核检查资源:内核发现键盘缓冲区中没有数据(用户还没输入)。
  4. 状态改变与队列操作
    • 内核将进程的 stateTASK_RUNNING 修改TASK_INTERRUPTIBLE
    • 内核将进程的 task_struct 从运行队列中摘下
    • 内核将进程的 task_struct 加入到“键盘输入等待队列”中。
  5. 调度切换:内核调用调度器,切换到另一个就绪的进程运行。
  6. 事件发生:用户按下键盘,触发硬件中断。键盘中断处理程序工作,将按键数据放入缓冲区。
  7. 唤醒进程:中断处理程序调用 wake_up 函数,它会找到“键盘输入等待队列”中的进程,并:
    • 将其 state 修改TASK_RUNNING
    • 将其 从等待队列中摘下
    • 将其 重新放回运行队列
  8. 再次被调度:在未来的某个时刻,调度器再次选中这个进程,它就从 scanf 系统调用中返回,继续执行,并读取到输入数据。

d. 关于“挂起(Suspended)”

您还提到了“挂起”,这是一个很好的延伸。挂起通常不是Linux内核原生的一个状态宏,而是一种行为,通常发生在内存压力很大时。

  • 行为:内核需要腾出物理内存(RAM)。它会选择一个处于阻塞状态TASK_INTERRUPTIBLETASK_UNINTERRUPTIBLE)的进程,将其占用的内存数据交换(Swap Out)到磁盘上。此时,这个进程除了在等待队列里,它的代码和数据都不在内存中了。
  • 状态:它的 state 可能仍然是 TASK_INTERRUPTIBLE,但它多了一个“被换出”的属性。当它等待的事件到来(被唤醒)时,内核首先需要把它的内存数据从磁盘换回(Swap In)到内存,然后才能将其加入运行队列,这个过程会有明显的延迟。

二、进程优先级

2.1基本概念

进程优先级 是一个决定进程如何被 CPU 调度器对待的关键属性,理解优先级的核心是:在多个可运行(RUNNABLE)的进程竞争 CPU 时,优先级高的进程有更大的机会被调度器选中执行

抽象化的理解:我们车站排队,排队的本质是确认优先级,确认了优先级,我们就能知道什么时候可以上车。

优先级的本质是:衡量得到某种资源的先后顺序。同样地,对进程来讲:优先级是进程得到 CPU资源的先后顺序

  • 优先级:能得到资源(先后问题)
  • 权限:是否得到某种资源

2.2查看系统进程

在这里插入图片描述

我们很容易注意到其中的⼏个重要信息,有下:

• UID:执⾏者的⾝份(user id)

• PID:进程的代号

• PPID:进程是由哪个进程发展衍⽣⽽来的,亦即⽗进程的代号

• PRI:进程可被执⾏的优先级(程序被CPU执行的先后顺序),其值越⼩越早被执⾏(默认80,且始终为默认值)

• NI:进程的nice值,进程优先级的修正数据(默认0)

进程的真实优先级PRI(new) == PRI(old默认) + NI


在这里插入图片描述


2.3 PRI vs NI

  • 需要强调⼀点的是,进程的nice值不是进程的优先级,他们不是⼀个概念,但是进程nice值会影 响到进程的优先级变化
  • 可以理解nice值是进程优先级的修正修正数据
  • 所以,调整进程优先级,在Linux下,就是调整进程nice值,nice其取值范围:[-20, 19],⼀共40个级别。Linux进程优先级(PRI):[60, 99]

在这里插入图片描述


在这里插入图片描述


2.4 查看进程优先级的命令

⽤ top 指令更改已存在进程的nice:

• top

• 进⼊top后按“r”‒>输⼊进程PID‒>输⼊nice值

注意:

• 其他调整优先级的命令:nice,renice

• 系统函数:


三、 优先级存在的意义

“进程优先级”的存在绝非偶然,它是解决计算机系统中资源竞争任务多样性之间矛盾的根本性方案

“我们”为何在火车站要排队?进程又为何在 CPU 上排队呢?

目标资源稀缺,导致要通过优先级确认谁先谁后的问题!


3.1 应对任务的重要性差异(关键型任务优先)

不是所有任务都生而平等。系统必须能够区分哪些任务关乎全局,哪些任务只是锦上添花。

  • 例子
    • 高优先级键盘输入处理。当你按下 Ctrl+C 试图终止一个失控的程序时,系统必须立即响应这个中断,否则用户会失去对机器的控制。如果键盘处理程序和后台的文件压缩任务平等竞争CPU,用户可能会感觉系统“卡死”了。
    • 低优先级后台文件备份、软件更新、病毒扫描。这些任务很重要,但不需要立即完成。它们可以悄无声息地在系统空闲时利用资源,而不影响你的正常工作。
  • 目的:确保关键任务总能得到及时响应,维持系统的可用性和响应性。

3.2 满足任务的时效性要求(实时性任务优先)

某些任务有严格的时间期限(Deadline),错过期限会导致结果错误或完全失效。

  • 例子
    • 视频播放:解码器必须在下一帧需要显示之前完成解码工作,否则视频就会卡顿。
    • 工业控制:机器人传感器数据处理必须在几毫秒内完成,并发出控制指令,否则可能导致生产事故。
    • 音频处理:声音缓冲区必须被及时填充,否则会产生刺耳的爆破音。
  • 目的:通过赋予实时进程最高的优先级,保证时间敏感型任务能够抢占CPU,按时完成。这就是实时优先级(1-99)存在的意义。

3.3 提升整体系统效率和用户体验(CPU空闲时间利用)

系统资源(尤其是CPU)经常会出现短暂的闲置状态。优先级机制允许系统**“见缝插针**”地利用这些碎片时间。

  • 例子:你在编辑文档时,每次思考或停顿的瞬间,CPU利用率都会骤降。一个低优先级的进程(如索引服务的文件索引器)就可以在这个时候被调度运行,进行一些计算。
  • 目的最大化资源利用率。让低优先级任务填充高优先级任务之间的空闲时间,从而提升系统的整体吞吐量,同时还不会让用户感觉到任何卡顿。

3.4 实现资源的合理分配与公平性(防止饿死)

如果没有优先级,所有进程完全平等地分享CPU时间(即“轮转”调度),对于一些需要大量计算的后台任务来说是公平的,但对于需要交互的用户来说却是灾难性的。你的每次点击可能都要等一个时间片轮完才能被响应。

优先级机制引入了一种受控的不公平

  • 交互式进程(如桌面、浏览器、IDE)被赋予较高优先级,从而获得更快的响应速度,提升用户体验。
  • 批处理进程(如编译大型项目、科学计算)被赋予较低优先级,它们可以充分利用系统空闲资源,但不会拖慢前台工作。

同时,调度器(如CFS)会保证即使是最低优先级的进程(Nice +19)也能最终获得一定的CPU时间,防止其被完全“饿死(Starvation)”。

即,优先级设立不合理[大量等级差过大进程],会导致优先级低的进程长时间得不到 CPU资源,进而导致:进程饥饿


3.5 匹配任务的资源需求特征(I/O消耗型 vs. CPU消耗型)

进程通常分为两类:

  • I/O消耗型进程:大部分时间在等待I/O操作(如磁盘读写、网络请求)。例如,Web服务器、文本编辑器。它们需要的是在I/O完成时能被快速响应,以便发起下一个请求。因此,它们应该被赋予较高优先级。
  • CPU消耗型进程:大部分时间在进行数学计算。例如,视频编码、数据建模。它们一旦获得CPU就会长时间占用。它们应该被赋予较低优先级,以免影响系统的交互性。

优先级机制帮助调度器更好地识别和区分这两种类型的进程,并采取不同的调度策略。


一个生动的比喻

你可以把CPU想象成一个医院的急诊室

  • 实时优先级进程:是生命垂危、需要立即抢救的病人(如心脏骤停)。他们拥有最高优先级,一来就必须立刻处理,所有医生都要围上来。
  • 高优先级进程(Nice值低):是突发急症的病人(如高烧、严重外伤)。他们需要很快被医生接诊。
  • 普通优先级进程(Nice=0):是普通急诊病人(如感冒、肠胃炎)。按挂号顺序排队等候。
  • 低优先级进程(Nice值高):是来做体检或打疫苗的人。他们不紧急,可以在所有急诊病人都处理完后,或者夜深人静医院空闲时再来。

如果没有这个优先级分诊系统,让所有病人(进程)完全平等地排队,那么生命垂危的病人可能还没排到就去世了(系统无响应)。而有了优先级,医院(操作系统)就能最大限度地拯救生命(保证关键任务),同时又能高效地处理所有病人(提升整体吞吐量),并确保每个人最终都能得到治疗(防止饿死)。

因此,进程优先级是现代操作系统中一项至关重要的机制,它优雅地平衡了效率、响应性、公平性和资源利用率这多个相互冲突的目标。


在这里插入图片描述
🌟 各位看官好我是工藤新一¹呀~

🌈 愿各位心中所想,终有所致!


网站公告

今日签到

点亮在社区的每一天
去签到