4. 进程优先级、进程切换与进程切换

📅 2026/7/17 21:02:58
4. 进程优先级、进程切换与进程切换
目录一、进程优先级1.1. 什么是进程的优先级1.2. 优先级和权限1.3. 查看进程的优先级1.4. 修改进程的优先级1.4.1. top1.4.2. renice1.4.3. 查看可以修改的极值1.4.4. 补充概念-竞争、独立、并行、并发二、进程切换2.1. 前置知识2.2. 具体的实现三、以Linux2.6内核介绍进程调度3.1. 实时操作系统和分时操作系统3.2. struct task_struct queue[140]3.2.1. 优先级和下标的关系3.2.2. 活跃进程的过程3.3. nr_active 和 unsigned int bitmap[5];3.3.1. nr_active3.3.2. unsigned int bitmap[5]3.4. active_queue 和 expired_queue一、进程优先级1.1. 什么是进程的优先级CPU 资源分配的先后顺序就是指进程的优先权priority。当目标资源短缺的情况下 需要通过优先级来确认谁先谁后的问题优先级是通过数字来表示的其中值越低优先级也就越高反之优先级越低。基于时间片的分时操作系统考虑公平性优先级可能会发生变化但是变化幅度不能太大。1.2. 优先级和权限在讲解查看和修改进程的优先级之前我们先讲一个知识点优先级和权限优先级解决的是得到资源后执行的顺序的问题权限解决的是能否得到资源的问题我们还是使用上一个博客中的代码进行讲解int main() { pid_t id fork(); if (id 0) { cerr 创建子进程失败 endl; return 1; } else if (id 0) { // child while (true) { sleep(1); cout 我是子进程 当前 pid: getpid() , 父进程id: getppid() endl; } } else { // father int count 5; while (count--) { sleep(1); cout 我是父进程 当前 pid: getpid() , 父进程id: getppid() count: count endl; } } return 0; }在另一个终端执行这个命令方便监控while :; do ps al | head -1; ps al | grep myprocess; sleep 1; done其中的 UIDuser id表示是谁拥有的但是这个是数字怎么看到具体是那个用户拥有呢在文件所在目录 输入以下命令ls -l ls -ln-n能变成数字的都变成数字其中我们发现xxhh 这个用户对应的数字是 998那么就知道上面运行的 myprocess 进程 的 uid 是 xxhh 用户。其中系统是怎么知道访问文件的时候是拥有者所属组还是 other?在访问文件的时候就是开启一个进程这个进程有 uid通过比较这个文件的拥有者的在比较是否是所属组最后比较 other。Linux系统中访问任何资源都是进程访问进程代表用户不管是在手机还是电脑上本质都是进程我们用户和系统打交道只能通过进程来交互所有的请求和交互都会变成进程所以说识别权限不是识别用户的而是识别进程和文件的权限的1.3. 查看进程的优先级其实在上面的 while :; do ps al | head -1; ps al | grep myprocess; sleep 1; done 命令中已经看到对应的内容了PRI进程的优先级默认是 80NI进程优先级的修正数据称为 nice 值进程的真实的优先级 PRI默认80 NI1.4. 修改进程的优先级1.4.1. top输入top进入top后按 “r” - 输入进程PID - 输入nice值这次换一个简单的代码int main() { while (true) { cout hello linux endl; sleep(1); } return 0; }操作步骤输入while :; do ps -al | head -1; ps -al | grep myprocess; sleep 1; done并执行切记切记一定要在 al 前加入 -不然的话 PRI 就不是 80了具体的可以问AI执行./myprocess查看输入 top接下去按下 R/r 之后就会跳出来下面这句话这里的renice指的就是要重新修改进程此时shell正在等待我们输入进程的PID值那我们就可以输入打印出来的 pid然后呢我们看到 shell 又在等待我们输入需要更改的nice 值输入要修改的值即可比如这里我输入10最后发现pri 和 NI 的值都发生了改变1.4.2. renicerenice [-n] 优先级 [-p] PID1 [-p] PID2 ...n指定要设置的 Nice 值也可以省略不写。-p指定进程 PID如果直接跟 PID-p 可以省略。输入while :; do ps -al | head -1; ps -al | grep myprocess; sleep 1; done并执行切记切记一定要在 al 前加入 -不然的话 PRI 就不是 80了具体的可以问AI执行./myprocessrenice 10 1134308查看结果如果想要调高优先级程度即输入负数需要使用 sudo 权限1.4.3. 查看可以修改的极值做实验nice 范围: [-20,19]PRI 默认值是 80PRI 的范围 [60,99] 差 40优先级设立不合理会导致优先级低的进程长时间得不到 CPU 的资源进而导致进程饥饿1.4.4.补充概念-竞争、独立、并行、并发竞争性系统进程数目众多而 CPU 资源只有少量甚至 1 个所以进程之间是具有竞争属性的。为了高效完成任务更合理竞争相关资源便具有了优先级独立性多进程运行需要独享各种资源多进程运行期间互不干扰并行多个进程在多个 CPU 下分别同时进行运行这称之为并行并发多个进程在一个 CPU 下采用进程切换的方式在一段时间之内让多个进程都得以推进称之为并发二、进程切换CPU上下文切换其实际含义是任务切换或者CPU寄存器切换。当多任务内核决定运行另外的任务时它保存正在运行任务的当前状态也就是CPU寄存器中的全部内容。这些内容被保存在任务自己的堆栈中入栈工作完成后就把下一个将要运行的任务的当前状况从该任务的栈中重新装入CPU寄存器并开始下一个任务的运行这一过程就是context switch。2.1. 前置知识1. 关于死循环进程的两个知识a. 一旦一个进程占有 CPU会把自己的代码跑完吗不会因为时间片b. 死循环进程不会打死系统不会一直都占有 CPU2. CPU 和 寄存器3. 如何调度的举个例子现在张三大三了想去参军入伍首先先要找导员去报备给学校学需要保留学籍。两年以后张三退伍回来张三先要去找导员导员给学校一说恢复学籍继续上学。在这个故事里学校 --- CPU导员 --- 调度器调度和切换张三 --- 进程学籍 --- 进程运行的临时数据CPU 内寄存器里面的内容当前进程的上下文数据保留学籍 --- 保存进程上下文数据把进程数据保存到 CPU 内寄存器。恢复学籍 --- 恢复上下文进程数据把 CPU 内寄存器里面的数据恢复给进程去当兵 --- 进程从 CPU 剥离调度和切换合并成为调度器。功能切换 和 选择进程2.2. 具体的实现进程切换最核心的就是保存和恢复当前进程的硬件上下文的数据即CPU内寄存器的内容当前进程要把自己的进程硬件上下文数据保存在哪里保存到进程的task_struct里面 是在TSS任务状态段如何区分全新的进程 和 已经调度过的进程task_struct里面有个变量int inrunningCPU在执行进程的时候跟PCB关系不大主要看代码和数据寄存器会填充 代码和数据的临时值或者状态pc会存储下一条指令的地址内核代码中的展示三、以Linux2.6内核介绍进程调度3.1. 实时操作系统和分时操作系统分时操作系统一个os说进程只能运行1ms如果没有跑完就会从CPU拿下来需要去运行队列中重新排队。单次给的时间1ms或者10ms称为时间片。实时操作系统主要用于工业或者新能源汽车上。例如辅助驾驶发现这个前方有行人就会刹车而不是说我刹车1ms如果刹不住就不刹车了。3.2. struct task_struct queue[140]以上面为模版然后画图3.2.1. 优先级和下标的关系调度的核心难题CPU 要从多个进程中选一个运行调度器你笔记里的‘导员’每次都要做选择。如果系统里有成百上千个进程调度器总不能一个一个去问‘你优先级是多少’吧那样效率太低系统会卡死。所以调度器必须要有一种‘秒级’的定位能力。引入什么数据结构计算机里什么结构定位最快数组因为它是通过下标直接取数时间复杂度是 O(1)。所以 Linux 内核干脆就准备了一个包含 140 个元素的指针数组 rq[140]数组的每一个格子都挂着对应优先级的进程链表。下标即优先的核心映射既然用了数组那问题就变成了如何把进程的优先级变成一个合法的数组下标最聪明的做法就是让优先级数字直接等于数组下标。调度器拿到一个进程只要看一眼它的优先级数字就能直接去数组的对应位置把它揪出来运行。直接进行推导“这时候我们再回头看普通进程的 Nice 值-20 ~ 19。为了把它转成合法的正数下标避开前 100 个给实时进程用的位置内核定了一个基准值Base120。公式就顺理成章地出来了数组下标 120 Nice 值。Nice 0 (默认) - 去 runqueue[120] 找Nice -20 (默认) - 去 runqueue[100] 找Nice 19 (默认) - 去 runqueue[139] 找优先级数值越小对应的数组下标越小位置越靠前调度器遍历的时候就越先遇到它。按照上述的 推理过程也可以推出 PRI 的公式 PRI x - 40 (140 - 40)x - 40 是相对偏移量这一步算的是这个进程在“普通优先级队伍”里的相对位次。140 - 40计算真实数组的起始下标真实下标 普通区域起始下标(100) 相对偏移量(x - 60)3.2.2. 活跃进程的过程活跃进程的图片整体上看是按照数组下标顺序依次执行微观上看每个具体的数组内容是按照队列先进先出的顺序执行3.3. nr_active 和 unsigned int bitmap[5];在上面执行的流程中有一个很详细现象内核是如何快速找到要执行任务的数组下标nr_active 和 unsigned int bitmap[5] 就是干这个的3.3.1.nr_activenr_active整个队列中有多少个进程nr_active 0 就需要 unsigned int bitmap[5] 进行查询3.3.2.unsigned int bitmap[5]bitmap 是位图每一个比特位都是一个值下面是 一个示意图我们发现 数组下标 为2和 6的 都有 进程因此 bitmap 对应的比特位 就是 1如图所示。综上的查询流程是调度器被触发后首要动作是检查全局计数器 nr_active。这个变量记录了整个运行队列中所有等待执行的进程总数相当于一个“总阀门”——若 nr_active为零说明 CPU 此时无需处理任何任务直接进入空闲状态只有当 nr_active大于零时调度器才会确认存在待调度的进程从而启动下一步的精确检索。确认有进程在等待后调度器会利用优先级位图 bitmap[5]共 160 个比特位覆盖 140 个队列进行 O(1) 级别的查找。它从数组的第一个元素开始快速跳过值为零的整型段找到第一个非零的 32 位段后再借助硬件指令从该段的最低有效位向高位扫描找出第一个置 1 的比特位。通过“段索引乘以 32 加比特位偏移”计算出具体的数组下标该下标就是当前优先级最高数值最小的非空队列位置调度器直接提取该队列的头部进程并执行上下文切换。因此查询进程就做到了近乎时间复杂度 O(1)的算法3.4.active_queue 和expired_queueactive指针永远指向活动队列expired指针永远指向过期队列现在有一个优先级为 120和 130 的进程优先级低的先执行 120 队列中的进程先执行1ms 执行之后去哪回到原来地方不对如果回到原来的地方就会造成大问题就是优先级为 120 队列中的进程会一直运行直到运行完毕就会造成其他进程的进程饥饿所以又有一个进程队列用来维护这个就是 expired 指向的线程此时优先级为 120 队列 仍然会回到 下标为 120 的数组中先进先出以此类推活动队列上的进程会越来越少过期队列上的进程会越来越多因为进程时间片到期时一直都存在的。没关系在合适的时候只要能够交换active指针和expired指针的内容就相当于有具有了一批新的活动进程