[操作系统] 进程的调度

news2025/1/21 15:58:37

进程切换概念

时间⽚:当代计算机都是分时操作系统,没有进程都有它合适的时间⽚(其实就是⼀个计数

器)。时间⽚到达,进程就被操作系统从CPU中剥离下来。

死循环是如何运行?

当一个进程代码为死循环,它并不会一直占据CPU资源。

在分时操作系统中有时间片的概念,每一个进程会占据CPU资源固定时间,如果在固定时间结束后进程尚未完成,则重新进入队列等待被调度。

CPU中寄存器对于进程切换

什么是寄存器

  1. CPU内有多个寄存器,每个寄存器作用都不同
  2. 寄存器就是CPU内部的临时空间,存放正在运行的进程的临时数据
  3. 寄存器不包含寄存器内的数据,寄存器只是临时空间!

比如说当计算1 + 1时,两个1单独存放在不同的寄存器中,add存放在一个寄存器中,然后计算后再返回值。

问题引入与解决

那么寄存器对于进程的切换起到了什么作用?

因为寄存器是用来存放正在运行的进程的临时数据,所以每个时刻的寄存器的数据对应的就是当前时刻该进程运行到哪一步了,包括执行到代码哪一行等信息。

所以在每个task_struct中就加入了tss_struct类型的成员,该结构体用来存放整型值,对应CPU寄存器的数据类型。已知进程是在时间片的限制下进行执行,所以在当前时间片结束后,CPU中的存放着当前运行程序的寄存器的所有数据就会在当前PCB的task_structtss_struct里面拷贝数据,在该进程下次被运行的时候就会将tss_struct中的拷贝数据再次拷贝到CPU寄存器中,使其可以从上次结束的状态继续运行,这就是寄存器对进程的切换所起作用。当然tss_struct也有关于当前进程是否结束,是否需要将寄存器数据拷贝等信息,避免资源浪费。

tss_struct源码:

以下是kernel 0.11task_struct的代码。

其中绿色框所圈部分即为tss_struct

上图即为tss_struct中的成员变量,对应各个寄存器。

操作系统中的 current 指针

在操作系统中,current指针也常常用于指向当前正在执行的进程或线程。例如,在内核代码中,current指针可能指向当前正在执行的进程控制块(PCB)。

// 例:操作系统内核中的 current 指针
struct task_struct* current = get_current_task(); // 获取当前进程

在多任务操作系统中,current指针通常指向当前正在执行的进程,这对于进程调度和上下文切换至关重要。

具体总结

寄存器的内容又叫做进程的上下文数据,**TSS**就是任务状态段。

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

总的来说整体进程切换过程如下:

  • 当前进程(例如进程A)运行并占用CPU资源,运行时其相关的上下文保存在寄存器和内存中。
  • 进程A被中断或主动让出CPU资源,操作系统将其上下文保存在进程A的PCB中。
  • 操作系统调度另一个进程(如进程B),并将进程B的上下文加载到CPU中。
  • 进程B开始执行,直到再次发生进程切换。

加载进CPU就是current指针指向该进程的task_struct

内核(Linux2.6)进程<font style="color:rgb(31,35,41);">O(1)</font>调度队列

分时操作系统和实时操作系统的基本概念和区别

分时操作系统

分时操作系统(Time-sharing Operating System) 是一种多任务操作系统,它允许多个用户共享计算机的处理能力。其主要特点是时间共享,即将CPU时间划分为多个时间片(时间段),每个任务在其分配的时间片内运行,然后轮流切换其他任务。

  • 任务调度:分时操作系统根据任务的优先级和需要的时间片来调度任务。它通过时间片轮转机制保证各任务公平地使用CPU。每个任务运行一定的时间片后,被暂停,操作系统会切换到另一个任务。
  • 响应性:由于其时间共享机制,用户可以感受到几乎同时的多任务处理效果,这适用于那些需要交互操作的环境,如多人使用的计算机系统。

特点:

  1. 任务切换:CPU在不同任务之间切换,确保多个任务可以并发执行。
  2. 用户交互:适用于需要较强实时响应的场景,如用户与计算机交互。
  3. 应用领域:分时操作系统通常用于多用户环境中,例如大型机、主机等。

实时操作系统

实时操作系统(Real-Time Operating System,RTOS) 是一种专门为时间敏感任务设计的操作系统。其目标是在确定的时间内(通常是毫秒级或微秒级)响应外部事件或任务请求,保证严格的时间约束。

  • 实时性要求:实时操作系统分为两种类型:
    • 硬实时系统:系统必须在严格的时间限制内完成任务,否则将导致系统失效(如无人驾驶汽车、航空航天系统、自动化控制等)。
    • 软实时系统:系统可以接受任务延迟,但需要在较短时间内完成(如视频流、语音处理等)。
  • 调度策略:实时操作系统通常采用更为严格的调度算法,以保证任务在指定时间内得到处理。常见的调度策略包括优先级调度、最短时间优先调度等。

特点:

  1. 高优先级任务:实时操作系统会根据任务的优先级来调度处理,确保重要任务能够优先完成。
  2. 时间敏感:对于实时性要求高的任务,系统必须确保在预定时间内响应。
  3. 应用领域:实时操作系统广泛应用于需要即时响应的领域,如嵌入式系统、工业控制、医疗设备、航空航天等。

但是大多数操作系统会将两种调度方式都包含,下文对调度队列的讲解会涉及。

内核优先级抢占

在分时操作系统中,进程可以根据优先级和调度策略被插入到**active**队列(即活跃队列),这意味着进程处于待执行状态,就是抢占。将进程插入到**expired**队列中,即进入就绪状态。

O(1)调度算法

一个CPU对应一个运行队列(**runqueue**,下文以一个单核CPU的调度过程来讲解调度算法。下图为Linux2.6内核中进程队列的数据结构。

runqueuearray[]作用特性

上图所示runqueuearray[0]array[1],对应的就是active队列expired队列,这两种队列特性相同,作用不同,所以该小节用来讲解关于其中的nr_activebitmap[5]queue[140],后文再对两种不同队列之间的关系和作用进行详细讲解。

queue[140]:

**queue[140]**用来实际链接PCB。

实际上,queue[140]是一个开散列的哈希表,在queue[140]中,每个下标存储的都是一个结构体指针task_struct*,当进程需要得到目标CPU资源的时候通过对应规则连接到哈希表中。

那么进程具体是如何连接到**queue[140]**中的呢?

queue[140]中,[0, 99] 的位置不做考虑,这些下标范围是为实时优先级提供,当前讲解分时操作系统通过优先级的调度。

由于[0, 99] 的位置不做考虑,所以目前queue[140]空余 40 个下标可以提供映射。又因为进程的优先级默认为80,而**nice:[-20, 19]****,**得优先级最小为60,最大为99,优先级的可调整范围为40。所以如果要计算对应映射位置可以通过(140 - 40) + (x - 60)得到在queue[140]中映射对应的下标值。

得到对应的哈希值(下标值)后,就会通过每个进程对应的优先级不同将task_struct链接至对应的由task_struct形成的链表中,遵循FIFO。

例如现在要将一个优先级为80默认值的进程插入:

  • 通过(140 - 40) + (x - 60),x = 80,计算可得映射位置为 120
  • 然后连接到120下标对应的队列中,等待执行。

bitmap[5]:

通过queue[140]直接进行管理进程的话流程如下:

  1. 从0下表开始遍历queue[140]
  2. 找到第⼀个⾮空队列,该队列必定为优先级最⾼的队列
  3. 拿到选中队列的第⼀个进程,开始运⾏,调度完成!

我们可以发现,遍历queue[140]时间复杂度是常数!太低效了!

为了使遍历更高效,引入了**bitmap[5]**,这就是位图

位图(Bitmap) 是一种数据结构,用于表示集合状态,常用于存储信息的标记,通常通过一组比特(bit)来表示每个元素的状态。每个比特位(bit)表示集合中某个元素的存在与否或状态的变化。位图利用一个二进制数组来表示一组数据或标志。每个比特位对应一个元素,如果该元素存在或处于某种状态时,对应的比特位被设置为1,否则为0。

我们通过位图是为了存储queue[140]中各个下标的状态,判断是否有链接的task_struct,以此来快速遍历,避免逐个查看,提⾼查找⾮空队列的效率。

bitmap[5]的类型为unsigned int,一个类型变量对应32比特位。由于queue[140]有140个下标需要标记,所以选择5个无符号整型,总共160比特位(4个太少,6个太多)。

nr_active:

用来存放总共有多少个运⾏状态的进程的总数量,直接判断当前是否有进程存在,避免位图查询。

调度器快速挑选一个进程?

先挑队列,再挑进程。

CPU调度器(CPU Scheduler) 是操作系统的一部分,负责决定哪个进程(或线程)在每个时刻能够使用 CPU 资源。实际上进行调度的操作都是由CPU调度器完成,在了解了大概得调度过程后,现在正式引入。

  1. 先查看nr_action,确认整个队列是否有进程存在,如存在则进行位图快速定位,否则挑选失败。
  2. 通过bit_map直接查询优先级最高的哈希值,然后通过queue查询;通过要管理的进程的哈希值在位图中查看当前哈希值对应的优先级队列是否存在进程,存在则通过queue查询并管理。
  3. 通过位图确认后,然后再queue通过下标哈希值快速定位队列,然后遍历队列来查找相关进程,或者在该队列队尾插入进程,或者是要执行该优先级的进程了,将头结点pop_front,然后将该PCB连接至current指针,执行切换算法。

回顾上图,现在已经理解了单组队列的大致调度过程。但是共有两组队列,一个是活动队列,一个是过期队列。通过runqueue中的***active*****expired**两个指针进行查找活动队列或是过期队列,*active对应的是活动队列,*expired对应的是过期队列。

这个两个队列分别有什么用?为什么需要用特定指针来指向对应队列,难道不是固定的吗?

下文对这两个问题进行探讨。

活动队列

活动队列是一个包含所有准备好等待执行的进程的队列。这些进程已经加载到内存并且处于就绪状态,能够立即执行,但由于CPU当前正被其他进程占用,因此这些进程需要等待 CPU 资源。

  • 进程状态:进程处于就绪状态时,表示它已经准备好运行,等待 CPU 分配时间。
  • 调度:活动队列中的进程会按照一定的调度策略(如时间片轮转、优先级调度等)进行调度。操作系统的调度器从活动队列中选择一个进程来分配 CPU。
  • 队列特性:活动队列中的进程按照一定的调度策略(例如 FIFO、优先级等)排队。队列的进程会根据调度策略进行轮换,确保公平或优先级较高的进程优先获得 CPU。

所以上小节所述的调度过程大部分其实是关于活动队列的,活动队列中的进程才是实际上与CPU资源交互的。

操作系统中的多个进程都需要运行,但如果只有一个 CPU。此时,所有等待执行的进程会排队在活动队列中,操作系统将通过调度算法从队列中选择一个进程连接current,然后进行进程切换,分配时间片。

过期队列

活跃队列表示当前CPU正在执行的运行队列,而 正在执行的运行队列(也就是活跃队列)是不可以增加新的进程的

所以操作系统设置了一个和活跃队列相同属性的过期队列,过期队列有以下作用:

  • 当活跃队列正在执行时如果有进程需要添加进运行队列,那么就会添加至过期队列当中。
  • 活跃队列中在CPU时间片执行后还未完全执行完的进程会存放进过期队列。

也就是说 活跃队列的进程一直在减少,而过期队列中的进程一直在增多!

但是<font style="color:rgb(51, 51, 51);">*active</font><font style="color:rgb(51, 51, 51);">*expired</font>的存在,二者不会一直保持差距扩大。

为什么要*active*expired(完整的O(1)调度算法实现)

调度器每次定位活动队列和过期队列时实际上是通过*active*expired的指向进行定位。其中*active指向活动队列,*expired指向过期队列。

在将<font style="color:rgb(51, 51, 51);">runqueue</font>关于调度的部分大致了解后,就可以形成一个完整的**<font style="color:rgb(51, 51, 51);">O(1)调度算法</font>**

  1. 首先是需要执行的PCB插入active queue,然后按照优先级顺序进行逐个连接current,CPU分配时间片运行。
  2. 在执行active queue中进程时,在一个时间片内未完全执行完成的进程和在该轮执行期间新的需执行的进程,插入expired queue中。
  3. 当前active queue中进程全部执行完后,active queue为空,expired queue存放着active queue未完全执行完成的进程和新进程。
  4. *active*expired两个指针互换指向(**swap(&active, &expired)**
  5. 调度器只会根据*active*expired进行判断是哪个队列,所以在交换后之前的expired queue变为了active queue,进行此轮执行。而之前的active queue变为了expired queue,在该轮执行的时候履行expired queue的职责。
  6. 然后重复循环该过程。

这就是完整的O(1)调度算法。

多核CPU的进程负载均衡问题

上文所述就是关于一个单核CPU的完整调度过程,但如果是多核CPU或者多个CPU时就应该考虑进程个数的负载均衡的问题。

上图所示runqueue中绿框所对应变量就是进程平均数量的负载因子。

负载均衡的策略

为了实现进程的负载均衡,操作系统通常采用以下几种策略:

1. 静态负载均衡

静态负载均衡通常在系统启动时设置,根据系统的硬件资源和进程的特点,将任务分配到多个CPU上。它不依赖于实时的负载变化,适用于负载预测较为准确的场景。

  • 均匀分配:将所有进程均匀分配到每个CPU上,确保每个CPU处理相同数量的进程。
  • 固定分配:系统按固定策略为每个CPU分配固定数量的任务,适合负载较为均衡的应用场景。
2. 动态负载均衡

动态负载均衡是在系统运行时动态调整进程的分配。根据当前各个CPU的负载情况,操作系统会实时调整进程的调度,确保负载的平衡。

  • 任务迁移:当一个CPU的负载过重时,操作系统会将一些进程迁移到负载较轻的CPU上,避免负载集中。
  • 负载监控:操作系统会实时监控各个CPU的负载情况,并根据需要调整进程的调度策略,以确保负载均衡。
3. 抢占式负载均衡

在抢占式调度策略中,操作系统可以动态地抢占正在运行的进程,并将其移到其他CPU上执行。这种方法可以确保每个CPU的负载始终处于平衡状态。

  • 进程迁移:如果一个CPU的负载过高,操作系统可以将一个正在运行的进程迁移到空闲的CPU上,从而达到负载均衡。
  • 实时调度:操作系统根据每个CPU的实时负载情况,动态调整进程的执行顺序,确保所有CPU都被合理利用。
4. 分级调度

在分级调度中,操作系统将调度任务划分为多个层次,分别处理不同类型的负载。例如,一个低优先级的任务可能被分配到一个负载较轻的CPU,而高优先级任务可能优先分配到负载较重的CPU。

  • 优先级调度:为不同的进程分配不同的优先级,优先级高的任务会被分配给空闲CPU。
  • 多级调度:根据任务的优先级和类型进行多级调度,以确保负载的动态平衡。

以上就是关于进程的调度的全部讲解。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2279888.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Biotin sulfo-N-hydroxysuccinimide ester ;生物素磺基-N-羟基琥珀酰亚胺酯;生物素衍生物;190598-55-1

一、生物素及其衍生物的概述 生物素衍生物是指在生物素&#xff08;Vitamin H或B7&#xff09;分子基础上进行化学修饰得到的衍生化合物。这些衍生化合物在生物医学研究、临床诊断和药物开发等领域有着广泛的应用。 生物素&#xff08;Biotin&#xff09;是一种水溶性维生素&a…

Jenkins-Pipeline简述

一. 什么是Jenkins pipeline&#xff1a; pipeline在jenkins中是一套插件&#xff0c;主要功能在于&#xff0c;将原本独立运行于单个或者多个节点的任务连接起来&#xff0c;实现单个任务难以完成的复杂发布流程。Pipeline的实现方式是一套Groovy DSL&#xff0c;任何发布流程…

Linux系统下安装配置Nginx(保姆级教程)

目录 前言 安装配置Nginx 一.下载依赖 二.下载Nginx 1. 访问官网?&#xff0c;获取需要的Nginx版本 2. 将文件下载到Linux系统 3. 解压文件 4. 解压成功后&#xff0c;当前文件夹会出现一个nginx-1.26.1文件夹&#xff0c;进入到文件夹内 5. 配置nginx 6.?编译并安…

《Linux服务与安全管理》| 邮件服务器安装和配置

《Linux服务与安全管理》| 邮件服务器安装和配置 目录 《Linux服务与安全管理》| 邮件服务器安装和配置 1.在Server01上安装dns、postfix、dovecot和telnet&#xff0c;并启动 2&#xff0e;在Server01上配置DNS服务器&#xff0c;设置MX资源记录 3&#xff0e;在server1上…

WPS数据分析000001

目录 一、表格的新建、保存、协作和分享 新建 保存 协作 二、认识WPS表格界面 三、认识WPS表格选项卡 开始选项卡 插入选项卡 页面布局选项卡 公式选项卡 数据选项卡 审阅选项卡 视图选项卡 会员专享选项卡 一、表格的新建、保存、协作和分享 新建 ctrlN------…

2025年免费量化交易软件——PTrade(含开通攻略)

量化交易软件&#xff0c;为广大投资者提供了一个便捷、高效的投资工具。 本文重点为大家介绍一款2025年好用的免费量化交易软件&#xff1a;PTrade量化&#xff0c;并详解其功能、特点、开通方法等。 一、PTrade的概念 PTrade是恒生电子开发的一款交易终端软件&#xff0c;旨…

【数据结构篇】顺序表 超详细

目录 一.顺序表的定义 1.顺序表的概念及结构 1.1线性表 2.顺序表的分类 2.1静态顺序表 2.2动态顺序表 二.动态顺序表的实现 1.准备工作和注意事项 2.顺序表的基本接口&#xff1a; 2.0 创建一个顺序表 2.1 顺序表的初始化 2.2 顺序表的销毁 2.3 顺序表的打印 3.顺序…

mysql查缺补漏

auto increment&#xff1a;自增序列&#xff0c;在字段后作为约束使用 comment&#xff1a;备注信息&#xff0c;用于在创建字段后或创建表的语句最后. 数值类型&#xff1a; 字符串类型&#xff1a; 日期类型&#xff1a; desc table_name&#xff1a;查询表结构 sho…

C++ 面向对象(继承)

三、继承 3.1 继承的概念 基于一个已有的类 去重新定义一个新的类&#xff0c;这种方式我们叫做继承 关于继承的称呼 一个类B 继承来自 类 A 我们一般称呼 A类&#xff1a;父类 基类 B类: 子类 派生类 B继承自A A 派生了B 示例图的语法 class vehicle // 车类 {}class …

JAVA-IO模型的理解(BIO、NIO)

前言 &#xff08;本文是作者学习制作rpc框架时&#xff0c;一些自用的笔记&#xff0c;并不会完整详细的介绍某个模块&#xff0c;会写大概的流程及一些相关概念&#xff0c;供日后复习使用~&#xff09; IO模型 先理解基本的IO流程&#xff1a; 应用A把消息发送到 TCP发送缓…

【Spring】原型 Bean 被固定

问题描述 在定义 Bean 时&#xff0c;有时候我们会使用原型 Bean&#xff0c;例如定义如下&#xff1a; Service Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) public class ServiceImpl { }然后我们按照下面的方式去使用它&#xff1a; RestController public class Hello…

奉加微PHY6230兼容性:部分手机不兼容

从事嵌入式单片机的工作算是符合我个人兴趣爱好的,当面对一个新的芯片我即想把芯片尽快搞懂完成项目赚钱,也想着能够把自己遇到的坑和注意事项记录下来,即方便自己后面查阅也可以分享给大家,这是一种冲动,但是这个或许并不是原厂希望的,尽管这样有可能会牺牲一些时间也有哪天原…

Python脚本实现通过JLink烧录Hex文件

1 安装JLink驱动程序 驱动安装包下载路径&#xff1a;https://www.segger.com/downloads/jlink/ 选择对应的版本下载&#xff1a; 将下载的安装文件双击进行安装。 2 安装 pylink 包 pip install pylink3 查询 JLink 设备的 serial number 将JLink通过USB线插入电脑。 w…

【Qt】04-Lambda表达式

前言一、概念引入二、使用方法2.1 基本用法代码示例2.2 捕获外部变量2.3 参数列表 三、完整代码mywidget.cppsecondwidget.cppmywidget.hsecondwidget.h 总结 前言 一、概念引入 Lambda表达式&#xff08;Lambda Expressions&#xff09;是C11标准引入的一种匿名函数对象&…

[STM32 HAL库]串口中断编程思路

一、前言 最近在准备蓝桥杯比赛&#xff08;嵌入式赛道&#xff09;&#xff0c;研究了以下串口空闲中断DMA接收不定长的数据&#xff0c;感觉这个方法的接收效率很高&#xff0c;十分好用。方法配置都成功了&#xff0c;但是有一个点需要进行考虑&#xff0c;就是一般我们需要…

汇编与逆向(一)-汇编工具简介

RadASM是一款著名的WIN32汇编编辑器&#xff0c;支持MASM、TASM等多种汇编编译器&#xff0c;Windows界面&#xff0c;支持语法高亮&#xff0c;自带一个资源编辑器和一个调试器。 一、汇编IDE工具&#xff1a;RadASM RadASM有内置的语言包 下载地址&#xff1a;RadASM asse…

Langchain+FastApi+Vue前后端Ai对话(超详细)

一、引入 首先可以先看下作者的文章 FastApi相关文章&#xff1a;创建最简单FastApi的项目Vue相关文章&#xff1a;最简单的aixos二次封装Langchain相关文章&#xff1a;如何使用LangSmith跟踪deepseek模型 二、后端搭建 1 项目文件结构 routers&#xff1a;存放api接口se…

leetcode49-字母异位词分组

leetcode 49 思路 通过一个哈希表进行记录每个分组&#xff0c;遍历strs&#xff0c;然后对每个字符串item进行排序&#xff0c;比如&#xff1a;acb bac cab都会被排序为’abc’,然后以abc作为map的key&#xff0c;value就是存放所有匹配出来为key的值&#xff0c;最后把ma…

深度学习 DAY1:RNN 神经网络及其变体网络(LSTM、GRU)

实验介绍 RNN 网络是一种基础的多层反馈神经网络&#xff0c;该神经网络的节点定向连接成环&#xff0c;其内部状态可以展示动态时序行为。相比于前馈神经网络&#xff0c;该网络内部具有很强的记忆性&#xff0c;它可以利用它内部的记忆来处理任意时序的输入序列&#xff0c;…

跨境电商使用云手机用来做什么呢?

随着跨境电商的发展&#xff0c;越来越多的卖家开始尝试使用云手机来协助他们的业务&#xff0c;这是因为云手机具有许多优势。那么&#xff0c;具体来说&#xff0c;跨境电商使用云手机可以做哪些事情呢&#xff1f; &#xff08;一&#xff09;实现多账号登录和管理 跨境电商…