【Linux】13:进程优先级和进程的切换与调度 目录一、优先级的基本概念1.1 什么是优先级1.2 为什么要有优先级二、如何查看并且修改进程优先级2.1 PRI2.2 NI2.3 优先级极限测试2.4 UID2.4.1 uid概念2.4.2 完整流程你怎么操作资源2.4.3 特殊情况进程 UID 会变化提权场景2.4.4 总结三、进程切换四、进程调度五、总结一、优先级的基本概念1.1 什么是优先级在日常生活中我们有很多排队的场景公路上等红灯在医院挂号或者是在食堂吃饭等等但是我们在公路上救护车可以无视红灯医院里急诊病人可以优先挂号以及食堂里老师肆意的插队…这些需要排队的事物就可以看作进程而像救护车急诊病人以及食堂的老师等等这些都是具有优先权的象征为什么要有优先权呢一定是有更重要的事情去做。所以此时就可以延申到进程 ---- 的优先级是什么进程要访问某种资源进程通过一定的方式排队确认享受资源的先后顺序。相信细心的你也发现了优先级不就是我们前面学习的 ---- 权限吗其实优先级和权限是有区别的权限决定的是 --- 能不能的问题而优先级是决定 --- 先后顺序的问题。1.2 为什么要有优先级如果我们食堂的窗口远大于人数如果我们自习室里的座位非常多人却非常少我们是不需要排队的也就是说当资源充足的时候我们并不需要排队。所以优先级产生的本质是因为 ----- 【资源相对比较少】所以呢优先权高的进程有优先执行权利有了优先级才能使得一些重要的进程被率先执行二、如何查看并且修改进程优先级知道了进程优先级的基本概念后我们来Linux中看看它到底是个什么东西呢我们可以对运行起来的进程使用下列指令去查看其优先级ps -al此处我们要关注的是以下几个重要信息UID: 代表执行者的身份PID :代表这个进程的代号PPID代表这个进程是由哪个进程发展衍生而来的亦即父进程的代号PRI代表这个进程可被执行的优先级其值越小越早被执行NI代表这个进程的nice值那我们重点来讲解一下这两个信息值对于 PRI 还是比较好理解的即 进程的优先级或者通俗点说就是程序被CPU执行的先后顺序此值越小进程的优先级别越高那 NI 呢? 就是我们所要说的 nice 值了其表示进程可被执行的优先级的修通俗简单一点来讲呢就是进程的 nice 值不是进程的优先级他们不是一个概念但是进程nice值会影响到进程的优先级变化。2.1 PRI测试代码如下#include stdio.h #include unistd.h #include sys/types.h int main() { while(1) { printf(我是一个进程我的pid为%d\n,getpid()); sleep(1); } return 0; }编译运行后我们使用ps -al来查看进程信息。如下所示红色框框里面的部分就是我们的进程和优先级。PRI 就是 priority 优先权的意思。而我们运行自己写的程序查询改进程的权限为80这也就说明Linux下进程的优先级本质就是数字。其实Linux下优先级是可以被修改的修改范围为[60, 99]这40范围内且进程的默认权限值是80。而优先级 数字越小表示该进程的 优先级越高2.2 NI我们清楚了进程的优先级是什么以及为什么接下来我们看一下到底该怎么做1运行上面的程序打印出进程的pid使用top命令进入到Linux任务管理器top#进入任务管理器如下所示2接下去按下R/r之后就会跳出来下面这句话这里的renice指的就是要重新修改进程此时shell正在等待我们输入进程的PID值那我们就可以输入上方的 3042483然后呢我们看到 shell 又在等待我们输入需要更改的nice值4输入要修改的值即可比如这里我输入10这个时候再使用ps查看就会发现进程的PRI这一项变为了90但是我们发现不仅仅是PRI这一栏变了还有一栏叫做NI的值也变了而且还刚刚好是我们输入的数值其实Linux系统支持用户调整优先级但并 不是让用户直接修改 pri 值而是修改 nice 值。而 nice 值不是优先级而是优先级的 修正数据。所以真正的优先级应该是pri pri(old) nice ;old指老的优先级所以我们刚刚修改的并不是优先级而是nice值。2.3 优先级极限测试进程的优先级真的只能在[60,99]这个范围呢移动吗如果不相信我们可以来做一下极值测试我们将程序的进程值调到尽可能的大nice值变为19pri变为99就是我们给出的最大范围。如果我们把nice值设置为-10我们发现进程的pri变为了70但是我们刚才不是刚把进程的优先级调至为99吗其实这是因为pri(old)是指老的优先级这个优先级永远是80所以再次调整时还是从80开始变化。我们把nice值设置为极小进程的pri果然为60所以nice值的范围就是 [-20, 19]。可能你会有疑问为什么要给进程优先级加上限制范围呢如果不加限制把自己的优先级调整的非常高别人的优先级非常低又因为OS是根据进程的pri来执行先后顺序的这样做可能就会打乱操作系统执行顺序。这样常规进程就很难分配到资源其实这就是一种 进程饥饿 的问题。为了尽量避免进程饥饿的问题现在的任何分时操作系统都是较为公平的进行调度。2.4 UID2.4.1 uid概念UID同样是一个重要的概念他表示的是用户的UID,也就是说明这个进程是谁的。我们可以使用id来查看UID对应的用户是谁。如下所示在linux系统中我也就是zhang或者1004是怎么访问资源的是我自己来访问嘛其实是我们写的进程来访问。你本机用户zhangUID1004不是你本人直接访问文件 / 网络 / 硬件是你启动的进程替你访问资源。 操作系统不认用户名zhang只认数字 UID1004所有权限校验全靠 UID/GID。2.4.2 完整流程你怎么操作资源你登录系统输入账号密码登录系统读取/etc/passwd绑定你的身份用户名zhang↔ UID1004、GID 对应组 ID。 登录后你的 shellbash/zsh进程运行身份就是 UID1004。你敲命令 / 运行程序生成新进程比如你执行cat test.txt、vim code.c、浏览器、服务程序 子进程会继承父进程 (shell) 的 UID、GID新进程有效身份依旧是 1004。进程发起资源访问内核校验 UID 权限当进程要读文件、创建文件夹、操作端口、读写磁盘时Linux 内核做权限判断文件自带属性属主 UID、属组 GID、rwx 权限位拿进程运行 UID (1004)和文件属主 UID 对比匹配使用文件「所有者权限」不匹配再对比 GID都不匹配则使用其他用户权限权限不足直接返回Permission denied举例子 文件a.txt属性-rw------- 1 zhang zhangUID1004 你运行cat a.txt进程 UID1004 → 匹配所有者可读可写。如果是 root 进程UID0访问root 拥有最高权限不受普通 rwx 限制。2.4.3 特殊情况进程 UID 会变化提权场景1su id 程序程序设置 SUID 权限后运行时进程 UID 会变成文件属主 UID不是启动者 UID。 例passwd命令属主是 root (UID0)带 SUID普通用户 zhang 运行它进程临时变成 UID0才能修改密码文件/etc/shadow。2sudo / su执行sudo ls /root 临时切换进程身份为 root (UID0)进程以 root 权限操作资源命令结束后身份恢复为 1004。2.4.4 总结人zhang只是触发程序运行真正和系统、文件、硬件交互的载体是进程 进程身上标记着数字 UID1004内核依靠这个 UID 判断该进程能不能访问对应资源。 没有进程单纯的用户账号无法读写任何系统资源。三、进程切换在Linux 操作系统中进程的调度与切换是 操作系统核心功能之一它涉及到如何有效地利用 CPU资源保证系统的响应速度和吞吐量。首先介绍4个关键的概念竞争性系统进程数目众多而CPU资源只有少量甚至1个所以进程之间是具有竞争属性的。为了高效完成任务更合理竞争相关资源便具有了优先级独立性多进程运行需要独享各种资源多进程运行期间互不干扰并行多个进程在多个CPU下分别同时进行运行这称之为并行并发多个进程在一个CPU下采用进程切换的方式在一段时间之内让多个进程都得以推进称之为并发我们知道 一个CPU在 同一时间只能运行一个进程而 -- 并发 -- 实际上就是 -- 利用时间片让每个进程在CPU上只能运行一个时间片的时间然后就被切换到另一个进程所以我们计算机虽然看起来似乎是非常流畅的运行每个进程而实际上则是一卡一卡的运行的只不过这个时间非常短我们感觉不到罢了。那进程首次调度完成 被切换走当 CPU 二次调度该进程时是如何记得上次执行到哪里了呢CPU中存在有大量的寄存器进程运行产生的临时数据都被保存在这些寄存器中这些临时数据被称为进程的硬件上下文当时间片消耗完的时候进程会保存这些上下文现阶段大家可以理解为保存到PCB中当进程被二次调度时进程会将曾经保存的硬件上下文进行恢复将之前保存的硬件上下文覆盖到CPU的寄存器中。进程切换包括以下几个关键步骤上下文保存 当操作系统决定要切换到另一个进程时首先需要保存当前进程的上下文信息包括程序计数器、寄存器内容、栈指针等。这些信息存储在进程的控制块PCB中。选择新进程 在确定要切换到哪个新进程之前操作系统会根据调度算法从就绪队列中选择一个合适的进程。这个选择可能基于进程的优先级、先到先服务FIFO、轮转法等。加载新进程的上下文 一旦确定了新进程操作系统就会从其对应的PCB中恢复该进程的上下文信息。这包括将新进程的程序计数器值加载到CPU中以便执行新进程的代码。总结虽然CPU中的寄存器只有一套但是寄存器内部保存的 数据可以有多套。CPU是被所有进程共享的但内部的数据却是进程私有的。大家可以理解为在任意一个时刻CPU中 的数据只属于一个进程。所以看起来我们用的是同一个设备但实际上进程之间是具有独立性的。四、进程调度进程调度是 操作系统根据一定的调度策略从就绪队列中选择下一个要执行的进程的过程。调度策略的选择会影响系统的性能、响应速度和资源利用率。这是Linux系统下对运行队列的设计。不考虑其他成员我们只看圈出来的两个部分红色部分为活动队列蓝色部分为过期队列他们两个你可以认为是完全相同的两个结构。进程队列数组 queue[140]这个数组用于存储不同优先级的进程队列。每个队列按照先进先出FIFO规则进行排队调度。数组的下标表示进程的优先级因此可以直接根据优先级来访问对应的进程队列提高了访问效率。进程队列状态位图 bitmap[5]为了快速判断哪些队列是非空的使用了一个位图来表示每个队列的状态。每个比特位对应一个队列如果该队列非空则对应的比特位为1否则为0。这样查找非空队列的操作变得高效时间复杂度为常数级别。active指针和expired指针这两个指针用于指示当前活跃队列和过期队列。随着调度的进行它们的内容可以交换从而实现活跃队列和过期队列的动态切换。活跃队列 和 过期队列活跃队列中包含当前活跃的进程而过期队列包含一段时间内未被调度的进程。Linux 内核根据需要从活跃队列和过期队列中选择进程进行调度以平衡优先级和资源利用效率。O(1) 调度算法 Linux 内核的调度器通常采用 O(1) 调度算法使用了位图bitmap来实现该算法在常数时间内选择下一个要执行的进程而不受进程数量的影响。这确保了调度器的高效性使得系统在任何负载情况下都能快速响应。什么是--- 活动队列 时间片还没有结束的所有进程都按照优先级放在该队列nr_active: 总共有多少个运行状态的进程queue[140]: 一个元素就是一个进程队列相同优先级的进程按照FIFO规则进行排队调度,所以数组下标就是优先级bitmap[5]:一共140个优先级一共140个进程队列为了提高查找非空队列的效率就可以用5*32个比特位表示队列是否为空这样便可以大大提高查找效率总结活跃队列 ---- 表示当前 CPU正在执行的运行队列而 正在执行的运行队列也就是活跃队列是不可以增加新的进程的。什么是 ---过期队列过期队列 和 活动队列 --- 结构一模一样过期队列上放置的进程都是时间片耗尽的进程、当活动队列上的进程都被处理完毕之后对过期队列的进程进行时间片重新计算总结某个处在活跃队列中的进程的时间片消耗完该进程就会从活跃队列中剥离然后被添加到过期队列。当活跃队列正在执行时如果有进程需要添加进运行队列那么就会添加至过期队列当中也就是说 活跃队列的进程一直在减少而过期队列中的进程一直在增多active和expired结构体指针 有什么用它们分别指向活跃队列和过期队列而活跃队列与过期队列由于属性完全相同于是被放在了一个叫做prio_arry_t[2]的数组里prio_arry_t[0]指向活跃队列prio_arry_t[1]指向过期队列当活跃队列被CPU执行完毕后我们只需要交换两个指针的内容即可这样仅仅是指向的内容变了活跃队列变为过期队列过期队列变活跃队列并且时间复杂度为 O(1)五、总结进程优先级资源有限催生优先级区分执行先后权限管 “能不能访问”优先级管 “谁先运行”。 PRI 真实优先级 60~99默认 80数值越小越优先nice 是修正值范围 - 20~19PRI80nice只能改 nice限制区间防止进程饥饿。top 命令 r 可修改 nice。UID 用户标识UID 是用户数字身份内核凭它校验文件权限root (UID0) 权限不受普通 rwx 约束。SUID 程序、sudo 能临时提升进程 UID。进程基础特性竞争抢 CPU独立资源隔离并行多 CPU 同时跑并发单 CPU 分时切换。进程切换上下文切换CPU 寄存器存进程上下文切走时存入 PCB重新调度时恢复一套寄存器多套进程私有上下文。Linux O (1) 调度active 活跃队列时间片未用完进程expired 过期队列时间片耗尽、新建进程。 内部 queue [140] 按优先级排队bitmap 快速找非空队列。active 耗尽直接交换双队列指针调度耗时恒定 O (1)。