目录
🔥前言🔥
💧进程切换💧
💧进程调度💧
🔥总结与提炼🔥
🔥共勉🔥
🔥前言🔥
在 Linux 操作系统中,进程的 调度 与 切换 是操作系统核心功能之一,它涉及到如何有效地利用CPU资源,保证系统的响应速度和吞吐量。
那么 Linux 是如何完成进程的调度与切换的呢? 本篇博客将会带大家一起了解一下 Linux 下的进程 调度 与切换。
💧进程切换💧
我们知道 一个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)来实现),该算法在常数时间内选择下一个要执行的进程,而不受进程数量的影响。这确保了调度器的高效性,使得系统在任何负载情况下都能快速响应。
什么是 --- 活动队列 ?
// 活动队列
struct q
{int nr_active;int bitmap[5];task_struct queue[140];
}
- 时间片还没有结束的所有进程都按照优先级放在该队列
- 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)
swap(&active,&expired);
🔥总结与提炼🔥
📒✏️总结:
- 进程切换最重要的部分就是 进程上下文的保护和恢复。
- 进程调度的优先级问题由 活跃进程数组的下标与进程优先级形成一种映射关系 解决。
- 进程调度的时间复杂度问题由 位图和两个结构体指针 解决,时间复杂度控制在了O(1)。
- 进程调度的进程饥饿问题由 活跃队列和过期队列 解决。
🔥共勉🔥
以下就是我对【Linux系统编程】进程的切换与调度 的理解,如果有不懂和发现问题的小伙伴,请在评论区说出来哦,同时我还会继续更新【Linux系统编程】,请持续关注我哦!!!