深入了解Linux系统—— 进程切换和调度

前言:

了解了进程的状态和进程的优先级,我们现在来看进程是如何被CPU调度执行的。

在单CPU的系统在,程序是并发执行的;也就是说在一段时间呢,进程是轮番执行的;

这也是说一个进程在运行时不会一直占用CPU直到运行结束,而是运行一段时间(时间片)然后切换下一个进程运行;

所以,对于一个死循环的进程执行的时候,我们是可以进行其他操作的,它并没有一直占有CPU

那这就要涉及到一个问题,那就是进程切换

对于一个进程,如果它在一个时间片内运行结束,那没有什么问题;但是如果一个时间片内没有运行结束呢?

那该进程被切换,然后等待再次被调度;

这里就有一个疑问:那再次调度进程时,如何知道上次运行到哪里呢?

寄存器

在叙述上面问题之前,先来了解一下寄存器

  • 寄存器是CPU内部的临时空间
  • 寄存器 != 寄存器中的数据

什么意思呢?

我们应该进程在运行的时候,它是会产生临时数据的,但是这些临时输出存放到哪里呢,就存放在CPU中的寄存器当中;

而寄存器又分为很多种,这里就不详细描述了;

这里简单理解:寄存器就是CPU内部的临时空间。

进程切换

思考一个问题:进程的时间片到了,被切换出CPU,在下次运行时,CPU是如何知道进程上次运行到哪里了,以及运行时产生的临时数据?

这里举个例子:(比较牵强,简单理解)

博主最近喜欢看一本小说,但是这本小说特别特别长,我今天看了200页,要休息了;但是博主又担心明天看时,忘记看到了哪一页,所以博主在博主搞了一个书签,记录下当前页数(第几页、第几行)。

但是第二天睡醒,博主大脑空空的,再打开这本书时,书签只记录了第几行第几页了,博主不知道这本小说里面都有什么人物了,没办法博主只能再从又开始看,了解小说人物;博主第二天看到了300页,要休息了,博主学精了,这次我不仅记录了当前第几页第几行,还记录了小说情节中出现的任务,还用其他细节的内容。

这样第三天博主再打开这本小说,虽然不记得第几页第几行,人物和小说细节,但是博主打开书签,上面记录了上次看完时小说的信息;这时就可以接着上次继续阅读了。

我们CPU也是如此,当一个进程被切换出CPU,下次被调度时,CPU是完全不记得上次进程运行到了哪里,有哪些临时变量等这些信息的;

那怎么办呢?

很简单,在我们一个进程被切换出CPU时,只需要记录一下上下文内容,和临时变量等这一系列信息即可;

这里又存在问题了:这些信息存放在哪里呢?

很显然,不会存放在CPU中;那就只能存在于进程的task_struct中。

Linux中,这些信息存放到了task_struct中的成员Tss中。

这样,当我们进程再次被调度时,直接将Tss中记录的上下文信息加载到CPU中的寄存器上,CPU就可以继续上次运行的位置继续去运行。

此外,为了区分首次被调度和已经被调度过的进程,task_struct中还存在一个整型变量来区分

在这里插入图片描述

进程调度

简单了解了进程是如何切换的,我们现在再来看进程是如何被调度的?

在了解进程状态时,提及到一个CPU一个运行队列,那进程不是按照队列顺序被调度的吗?

在这里插入图片描述

很显然不是,如果真的是如此,那进程优先级就没有存在的意义了。

我们现在来看,linux中运行队列的整体结构:

在这里插入图片描述

一眼望去,眼花缭乱的,为何如此复杂?

我们这里先注意看上图中的红色区域和蓝色区域内的内容;

运行队列的实现思路

我们先来看,红色区域和蓝色区域内都存在三个成员:

  • nr_active
  • bitmap[5]
  • queue[140]

queue

我们首先来看queue,它是一个数组一个140个元素;这里面存储的类型是struct task_struct*,可以理解为一个task_struct链表

它可以分为两部分:

  • [0 , 99]:这一部分表示的是实时优先级(暂时不探讨);
  • [100 , 139]:这一部分,每一个位置都对应一个优先级。

[100 , 139],一共40个位置,而我们优先级正好也是40个,所以这四十个位置和我们40个优先级一一对应。

这样每一个位置存放的都是优先级相同的进程的task_struct队列;

这样对于一个进程,我们只需要根据它的优先级就可以直接找到它对应的队列。(当然这里需要一个映射关系:对应位置 = (x - 60) + (140 - 100);)

那这样,我们的queue本质上不就是一个hash表吗(这个表中每一个位置存储的是对应优先级的task_struct队列)

bitmap[5]

在上述中,queue本质上上一个hash表,我们可以通过进程的优先级快速的找到其对应的队列;

但是,我们CPU要挑选一个进程运行,那还是要遍历一遍整个queue表啊;效率也不是很高啊

为了提高效率,这里存在一个bitmap[5]位图;

  • 它的类型是:unsigned int bitmap[5]

  • 我们知道一个字节等于8个bit位,一个整形4个字节;那一个整形用二进制表示就存在32位(32个0/1)。

  • 这里有5给个整形,也就是160个二进制数;而queue中一共存在140个位置,我们是不是可以用一个bit对应queue中的一个task_struct队列;

  • 所以这里bitmap[5]中每一个bit位如果为1就表示其对应位置的队列中存在task_struct;为0表示其对应位置不存在task_struct

所以,有了bitmap[5]CPU在选择进程运行时,就不需要再遍历queue表,而是在bitmap中找到bit位为1的位置,然后直接去其queue对应位置去寻找进程的task_struct即可。

这里检测的过程,简单来说急速对bitmap[5]中的5个元素通过int的方式进行位操作,(例如,bitmap[0] & xFFFFFFF == 0就表示bitmap[0]的二进制的每一位都等于0,就表示第一个整型位对应位置队列中是不存在task_struct的;一次类推);这样相比于去遍历queue表,效率快了非常对,已经接近O(1)了。

nr_avtive

这个就简单多了,它指的就是当前CPU队列中所有可运行队列的数量。

在这里插入图片描述

进程饥饿

进程饥饿(Process Starvation) 是指某个进程因长期无法获得所需的系统资源(如CPU时间、I/O资源等)而无法执行的现象。

简单来说就是应该进程它一直在等待被调度,但是一直没有被调度;

我们知道一个进程它运行时间片结束后,被切换出CPU,它是要重新回到等待队列,等待下一次被调度的;

在我们上述的理解中,应该优先级为60的进程,它是一个死循环,时间片到了,被切换出CPU之后,它又回到了优先级是60的队列中;那CPU在下一次调度时,还是有限调度优先级高的进程;此时还存在一个优先级为90的队列,那此时CPU就会一直调用优先级为60的进程,直到该进程执行结束,才会去调度优先级为90队列中的进程;

那如果这样去设计的话,非常容易就造成了进程饥饿的问题;

Linux中是如何去解决的呢?

Linux中,它不仅存在一个queue,在内核中,将queuebitmapnr_avtive包装成rqueue_elem;然后定义struct rqueue_elem[2]

这样一个struct rqueue_elem表示活动队列,另外一个struct rqueue_elem表示过期队列;

Linux内核中还存在两个指针,一个是active指向当前的活动队列,另一个expired指向当前的过期队列。(初始情况下:struct rqueue_elem[0]是活动队列,struct rqueue_elem[1]是过期队列;active指向rqueue_elem[0]expired指向rqueue_elem[1]

这样CPU只在active指向的活动队列中选取进程调度,进程被调度结束后,放入到expired指向的过期队列中。

这样,active执行的活动队列中进程被调度完时,CPU就要去调度expired指向的队列。

交换一下activeexpired的值,active就指向了新的活动队列;expired就指向了新的过期队列。

在这里插入图片描述

到这里,本篇文章大致内容就结束了,感谢各位支持

简单总结:

  • 进程切换:进程是如何切换的,切换时上下文信息存储到哪里
  • 进程调度:linux中的调度算符:O(1)调度算法实现的思路。

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

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

相关文章

阿里云服务迁移实战: 06-切换DNS

概述 按前面的步骤,所有服务迁移完毕之后,最后就剩下 DNS 解析修改了。 修改解析 在域名解析处,修改域名的解析地址即可。 如果 IP 已经过户到了新账号,则不需要修改解析。 何确保业务稳定 域名解析更换时,由于 D…

uni-app 中封装全局音频播放器

在开发移动应用时,音频播放功能是一个常见的需求。无论是背景音乐、音效还是语音消息,音频播放都需要一个稳定且易于管理的解决方案。在 uni-app 中,虽然原生提供了 uni.createInnerAudioContext 方法用于音频播放,但直接使用它可…

golang常用库之-标准库text/template

文章目录 golang常用库之-标准库text/template背景什么是text/templatetext/template库的使用 golang常用库之-标准库text/template 背景 在许多编程场景中,我们经常需要把数据按照某种格式进行输出,比如生成HTML页面,或者生成配置文件。这…

Linux btop 使用教程

简介 btop 是一个基于终端的现代系统资源监控器,具有美观的图形界面、响应快、功能丰富等特点。它支持查看 CPU、内存、磁盘、网络、进程,并可以方便地筛选和管理进程。 功能总览 启动命令: btop界面分为以下几部分: CPU 区域…

Vue3调度器错误解析,完美解决Unhandled error during execution of scheduler flush.

目录 Vue3调度器错误解析,完美解决Unhandled error during execution of scheduler flush. 一、问题现象与本质 二、七大高频错误场景与解决方案 1、Setup初始化陷阱 2、模板中的"幽灵属性" 3、异步操作的"定时炸弹" 4、组件嵌套黑洞 5…

使用DeepSeek定制Python小游戏——以“俄罗斯方块”为例

前言 本来想再发几个小游戏后在整理一下流程的,但是今天试了一下这个俄罗斯方块的游戏结果发现本来修改的好好的的,结果后面越改越乱,前面的版本也没保存,根据AI修改他是在几个版本改来改去,想着要求还是不能这么高。…

Kotlin带接收者的Lambda介绍和应用(封装DialogFragment)

先来看一个具体应用:假设我们有一个App,App中有一个退出应用的按钮,点击该按钮后并不是立即退出,而是先弹出一个对话框,询问用户是否确定要退出,用户点了确定再退出,点取消则不退出,…

ES6/ES11知识点 续一

模板字符串 在 ECMAScript(ES)中,模板字符串(Template Literals)是一种非常强大的字符串表示方式,它为我们提供了比传统字符串更灵活的功能,尤其是在处理动态内容时。模板字符串通过反引号&…

【C++】智能指针RALL实现shared_ptr

个人主页 : zxctscl 专栏 【C】、 【C语言】、 【Linux】、 【数据结构】、 【算法】 如有转载请先通知 文章目录 1. 为什么需要智能指针?2. 内存泄漏2.1 什么是内存泄漏,内存泄漏的危害2.2 内存泄漏分类(了解)2.3 如何…

ROS2 开发踩坑记录(持续更新...)

1. 从find_package(xxx REQUIRED)说起,如何引用其他package(包) 查看包的安装位置和include路径详细文件列表 例如,xxx包名为pluginlib # 查看 pluginlib 的安装位置 dpkg -L ros-${ROS_DISTRO}-pluginlib | grep include 这条指令的目的是…

系统思考:困惑源于内心假设

不要怀疑,你的困惑来自你的假设。 你是否曾经陷入过无解的困境,觉得外部环境太复杂,自己的处境无法突破?很多时候,答案并不在于外部的局势,而是来自我们内心深处的假设——那些我们理所当然、从未质疑过的…

GitHub修炼法则:第一次提交代码教学(Liunx系统)

前言 github是广大程序员们必须要掌握的一个技能,万事开头难,如果成功提交了第一次代码,那么后来就会简单很多。网上的相关资料往往都不是从第一次开始,导致很多新手们会在过程中遇到很多权限认证相关的问题,进而被卡…

沥青路面裂缝的目标检测与图像分类任务

文章题目是《A grid‐based classification and box‐based detection fusion model for asphalt pavement crack》 于2023年发表在《Computer‐Aided Civil and Infrastructure Engineering》 论文采用了一种基于网格分类和基于框的检测(GCBD)&#xff…

【Flask】ORM模型以及数据库迁移的两种方法(flask-migrate、Alembic)

ORM模型 在Flask中,ORM(Object-Relational Mapping,对象关系映射)模型是指使用面向对象的方式来操作数据库的编程技术。它允许开发者使用Python类和对象来操作数据库,而不需要直接编写SQL语句。 核心概念 1. ORM模型…

C/C++滑动窗口算法深度解析与实战指南

C/C滑动窗口算法深度解析与实战指南 引言 滑动窗口算法是解决数组/字符串连续子序列问题的利器,通过动态调整窗口边界,将暴力解法的O(n)时间复杂度优化至O(n)。本文将系统讲解滑动窗口的核心原理、C/C实现技巧及经典应用场景,助您掌握这一高…

Vuex使用指南:状态管理

一、什么是状态管理?为什么需要 Vuex? 1. 状态管理的基本概念 在 Vue 应用中,状态指的是应用中的数据。例如: 用户登录状态购物车中的商品文章列表的分页信息 状态管理就是对这些数据的创建、读取、更新和删除进行有效管理。 …

【信息系统项目管理师-论文真题】2007下半年论文详解(包括解题思路和写作要点)

更多内容请见: 备考信息系统项目管理师-专栏介绍和目录 文章目录 试题1:大型项目的计划与监控1、写作要点2、解题思路大型信息系统项目的组织制订大型信息系统项目进度计划的方法试题2:组织级项目管理的绩效考核1、写作要点2、解题思路在项目考核过程中会遇到哪些问题项目的…

项目管理学习-CSPM(1)

01引言 最近在学习CSPM的课程,有部分的内容自己还是受益匪浅的,建议有需要提升项目管理能力的同学可以以考促学的方式进行学习,下面整理了一部分内容和大家分享和学习。CSPM全称 China Standards Project Management,中文名项目管…

介绍分治、动态规划、回溯分别是什么?有什么联系和区别?给出对象的场景和java代码?

一、分治算法(Divide and Conquer) 概念: 分治算法是将一个复杂问题分成若干个子问题,每个子问题结构与原问题类似,然后递归地解决这些子问题,最后将子问题的结果合并得到原问题的解。 特点:…

解锁DeepSeek模型微调:从小白到高手的进阶之路

目录 一、DeepSeek 模型初相识二、探秘微调原理2.1 迁移学习基础2.2 微调的参数更新机制 三、数据准备3.1 数据收集3.2 数据标注3.3 数据预处理 四、模型选择与加载4.1 选择合适的预训练模型4.2 加载模型 五、微调训练实战5.1 确定微调策略5.2 设置训练参数5.3 训练过程 六、模…