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

【Linux】进程优先级切换调度

1. 进程优先级

1.1 基本概念

是什么?

是进程得到CPU资源的先后顺序

为什么?

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

优先级 vs 权限

优先级本质上是能得到资源,先后的问题。权限本质是是否能得到某种资源的问题。

怎么办?

也是一种数字。int,task_struct

值越低,优先级越高,反之,优先级越低。

基于时间片的分时操作系统,考虑公平性,优先级可能变化,但是变化幅度不能太大

1.2 查看系统进程

在linux或者unix系统中,用ps ‒l命令则会类似输出以下几个内容:

我们很容易注意到其中的⼏个重要信息,有下:
  • UID:即用户 ID,决定进程拥有的权限(如普通用户 UID≠0,无法执行修改系统配置等 root 操作),确保进程只能访问自身权限范围内的资源。
  • PID:进程唯一标识,内核通过 PID 管理进程(如kill PID终止进程、waitpid(PID)回收子进程),避免进程混淆。
  • PPID:父进程 ID,明确进程的 “血缘关系”,用于追溯进程来源(如通过 PPID 找到创建当前进程的父进程),也是孤儿进程被领养的判断依据。
  • PRI:静态优先级(内核设定,用户无法直接修改),直接影响 CPU 调度顺序,值越小越先被分配 CPU 时间片。
  • NI:nice 值(用户可调整,范围 - 20~19),用于动态修正 PRI(PRI = 基础 PRI + NI),NI 越小,PRI 越低,进程优先级越高(如nice -n -5 程序提升进程优先级)。

1.3 修改进程优先级的命令

1. ⽤top命令更改已存在进程的nice:

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

2. nice:启动新进程时指定优先级

作用:在启动进程的同时设置其 NI 值(优先级修正值)。
语法

nice -n [NI值] 命令

参数说明

  • NI值 范围:-20(最高优先级)~ 19(最低优先级),默认 0
  • 普通用户只能设置 0~19(降低优先级),-20~-1 需 root 权限(提高优先级)。

示例

# 以 NI=5 启动程序(降低优先级)
nice -n 5 ./myprogram# 以 NI=-3 启动程序(提高优先级,需 root)
sudo nice -n -3 ./myprogram

3. renice:调整已运行进程的优先级

作用:修改正在运行的进程的 NI 值。
语法

renice [新NI值] -p [进程PID]

参数说明

  • -p:指定进程的 PID(可通过 ps 或 top 查看)。
  • 权限限制同 nice:普通用户只能调高自身进程的 NI(降低优先级)。

示例

# 将 PID=1234 的进程 NI 改为 10(降低优先级)
renice 10 -p 1234# 将 PID=5678 的进程 NI 改为 -5(提高优先级,需 root)
sudo renice -5 -p 5678

1.4 补充概念-竞争、独立、并行、并发

竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级。
独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰。
并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行。
并发: 多个进程在⼀个CPU下采用进程切换的方式,在⼀段时间之内,让多个进程都得以推进,称
之为并发。

2. 进程切换

2.1 死循环进程如何运行

死循环进程(如while(1);)会持续占用 CPU,但操作系统通过时间片轮转调度限制其独占:

  • 内核为每个进程分配固定时间片(如几毫秒),死循环进程运行到时间片结束时,内核会触发时钟中断,强行剥夺其 CPU 使用权。
  • 进程被暂时挂起,放入调度队列,等待下一次被调度(重新获得时间片)。
  • 因此,死循环进程看似 “一直运行”,实则是在 “运行 - 挂起 - 再运行” 的循环中反复切换,与其他进程共享 CPU。

2.2 聊聊 CPU、寄存器

  • CPU:执行指令的核心硬件,通过 “取指 - 译码 - 执行” 循环处理程序指令。
  • 寄存器:CPU 内部的高速存储单元,用于临时存放数据和指令,包括:
    • 通用寄存器:存放临时数据(如变量值、运算结果);
    • 程序计数器(PC):记录下一条要执行的指令地址;
    • 栈指针(SP):指向当前栈顶位置;
    • 状态寄存器:保存 CPU 状态(如运算结果标志、中断屏蔽位)。
  • 进程运行时,其指令和数据需加载到寄存器中被 CPU 处理,寄存器状态是进程运行的 “即时快照”。

2.3 进程切换如何实现

进程切换(上下文切换)是内核将 CPU 从一个进程切换到另一个进程的过程,核心步骤:

  1. 保存旧进程上下文
    当进程时间片结束或被中断时,内核将旧进程的寄存器状态(PC、SP、通用寄存器等)保存到其 PCB(进程控制块)中。

  2. 选择下一个进程
    调度器根据优先级、时间片等策略,从就绪队列中选取下一个要运行的进程。

  3. 恢复新进程上下文
    从新进程的 PCB 中读取之前保存的寄存器状态,加载到 CPU 寄存器中,包括将 PC 设置为新进程的下一条指令地址。

  4. 切换完成
    CPU 开始执行新进程的指令,新进程进入运行态,旧进程则根据状态(如就绪、阻塞)进入对应队列。

进程切换最核心的是保存和恢复当前进程的硬件上下文数据,即CPU内寄存器的内容!

3. 进程调度

3.1 O (1) 调度器的核心结构与调度逻辑

心结构与逻辑

  • runqueue:每个 CPU 对应一个运行队列,管理该 CPU 上的可运行进程。
  • prio_array_t(活跃 / 过期数组)
    • 用 bitmap[5](位图)快速标记 “哪些优先级有进程”,queue[140] 按优先级存放进程链表(优先级范围 0 - 139,数值越小优先级越高)。
    • 调度时,通过位图 O (1) 时间 找到最高优先级的进程链表,再从链表取进程,实现高效调度。
  • 负载因子:基于 CPU 队列中进程平均数量,平衡多 CPU 间的负载。

O (1) 调度器通过 “位图 + 优先级链表”,能在常数时间内挑选进程,提升调度效率。

3.2 O (1) 调度器的双队列机制

3.2.1 固定优先级调度的饥饿问题

固定优先级且无动态优先级调整的进程调度机制下,若存在两个持续循环的进程:

  • 进程 A,优先级为 60(数值越小优先级越高);
  • 进程 B,优先级为 90。

当进程 A 每次用完分配的 CPU 时间片后,会被重新放回对应优先级(60)的就绪队列前端。由于调度器始终优先选取优先级更高的进程执行,进程 A 会持续抢占 CPU 资源。而优先级较低的进程 B,因进程 A 长期占据高优先级调度权且自身持续循环,将长期无法获得 CPU 时间,从而陷入进程饥饿状态,即长时间得不到调度执行的情况。

3.2.2 双队列机制

在 O (1) 调度器中,为解决固定优先级导致的饥饿问题,设计了 “活跃队列(active)” 与 “过期队列(expired)” 的双队列机制:

  • 活跃队列(active):存放当前可调度的进程,调度器从这里选取最高优先级进程执行。
  • 过期队列(expired):当进程用完时间片后,不会直接放回原优先级的活跃队列,而是被移至过期队列。

当活跃队列中的所有进程都耗尽时间片后,调度器会交换活跃队列与过期队列的角色(活跃变过期,过期变活跃),让原本在过期队列中的进程(包括低优先级进程)获得调度机会。

这种设计避免了高优先级进程长期垄断 CPU:即使进程 A(优先级 60)用完时间片后进入过期队列,待队列交换后,进程 B(优先级 90)也能在新的活跃队列中被调度,从机制上缓解了饥饿问题。

http://www.dtcms.com/a/393042.html

相关文章:

  • Ubuntu24上安装Scrapy框架实战
  • 正向shell,反弹shell学习
  • 一维数组原地更新——力扣119.杨辉三角形II
  • Python语法学习-1
  • Linux基础命令大全
  • 9.21 快速选择
  • 【常见集合】HashMap
  • Docker安装小白教程(阿里yum)
  • MySQL表结构变更详解:ALTER TABLE ADD COLUMN语法、最佳实践与避坑指南
  • 【LeetCode - 每日1题】设计电子表格
  • Spring 中 REQUIRED 事务的回滚机制详解
  • C++框架中基类修改导致兼容性问题的深度分析与总结
  • 学习笔记-SpringBoot项目配置
  • Java数据结构——时间和空间复杂度
  • 如何在接手新项目时快速上手?
  • Zynq开发实践(SDK之自定义IP2)
  • 数据库相关锻炼
  • PostgreSQL 入门与实践
  • pytorch基本运算-PyTorch.Tensor张量数据类型
  • 数据结构与算法 第三章 栈
  • Spring Boot 整合 MyBatis:从入门到企业级实践
  • FHook Java 层全函数 HOOK 框架
  • TDengine 聚合函数 STDDEV_POP 用户手册
  • 【 嵌入式Linux应用开发项目 | Rockit + FFmpeg+ Nginx】基于泰山派的IPC网络摄像头
  • 机器学习中的高准确、低召回
  • Go基础:Go基本数据类型详解
  • 项目管理(一)
  • 【STM8L101 执行函数FLASH_ProgramBlock出现问题】
  • ​​[硬件电路-278]:双向双电源电平转换收发器74AXP2T45DCH功能概述、管脚定义
  • 音视频同步的原理和实现方式