CPU分时、中断和上下文切换

2023-05-16

准备知识:

实时和分时

嵌入式操作系统可以分为实时操作系统和分时操作系统两类。我们现实之中使用的绝大多数是分时操作系统,比如windows或者linux。但是比如汽车就必须使用实时操作系统,举一个经常使用的实例,中高档汽车中使用的气囊。当报告车辆碰撞的传感器中断CPU后,操作系统应快速地分配展开气囊的任务,并且不允许任何其他非实时处理进行干扰,晚一秒钟展开气囊比没有气囊的情况更糟糕,这就是一个典型的必须使用硬实时的系统。而最近特斯拉处于风口浪尖,刹车不灵,就是因为刹车辅助系统没有及时开启,因为他使用的还是个分时系统。所以从技术上讲,特斯拉并不可靠。由于我们这里是个引子,我们不做多介绍。感兴趣的同学可以看看此文:https://blog.csdn.net/zhourui1982

中断

中断就是打断处理器当前的执行流程,去执行另外一些和当前工作不相干的指令,执行完之后,还可以返回到原来的程序流程继续执行。举个例子:“时钟中断”是特别重要的一个中断,利用晶振产生的方波信号输入,使得cpu按照这个频率去产生中断指令,也就是我们的时间片轮转调度算法的基础(时间片就是我们分配给每个任务的执行时间)。整个操作系统的活动都受到它的激励,系统利用时钟中断维持系统时间、促使环境的切换,以保证所有进程共享CPU,这使得我们同时使用多个应用程序成为可能;利用时钟中断进行记帐、监督系统工作以及确定未来的调度优先级等工作。可以说,“时钟中断”是整个操作系统的脉搏。总结:中断就是主动让出cpu的执行权限,让别的程序来执行。

 

内核态和用户态

操作系统在加载的时候,会把所管理的内存划分为两个区域,一些处于内核态(受到操作系统保护),一些处于用户态。用户态是不能直接访问内核态的地址空间的。对 32 位操作系统而言,它的寻址空间(虚拟地址空间,或叫线性地址空间)为 4G(2的32次方),也就是说一个进程的最大地址空间有4GB,其中,0-3G是属于用户空间,3-4G是内核空间。操作系统的核心是内核(kernel),它独立于普通的应用程序,可以访问受保护的内存空间,也有访问底层硬件设备的所有权限。为了保证内核的安全,现在的操作系统一般都强制用户进程不能直接操作内核。具体的实现方式基本都是由操作系统将虚拟地址空间划分为两部分,一部分为内核空间,另一部分为用户空间。针对 Linux 操作系统而言,最高的 1G 字节(从虚拟地址 0xC0000000 到 0xFFFFFFFF)由内核使用,称为内核空间。而较低的 3G 字节(从虚拟地址 0x00000000 到 0xBFFFFFFF)由各个进程使用,称为用户空间。

换句话说就是, 最高 1G 的内核空间是被所有进程共享的!

在用户态下,进程运行在用户地址空间中,被执行的代码要受到 CPU 的诸多检查,它们只能访问映射其地址空间的页表项中规定的在用户态下可访问页面的虚拟地址,且只能对任务状态段(TSS)中 I/O 许可位图(I/O Permission Bitmap)中规定的可访问端口进行直接访问。

内核有 4 项工作:

  1. 内存管理:追踪记录有多少内存存储了什么以及存储在哪里
  2. 进程管理:确定哪些进程可以使用中央处理器(CPU)、何时使用以及持续多长时间
  3. 设备驱动程序:充当硬件与进程之间的调解程序/解释程序
  4. 系统调用和安全防护:从流程接受服务请求

在正确实施的情况下,内核对于用户是不可见的,它在自己的小世界(称为内核空间)中工作,并从中分配内存和跟踪所有内容的存储位置。用户所看到的内容(例如 Web 浏览器和文件)则被称为用户空间。这些应用通过系统调用接口(SCI)与内核进行交互。举例来说,内核就像是一个为高管(硬件)服务的忙碌的个人助理。助理的工作就是将员工和公众(用户)的消息和请求(进程)转交给高管,记住存放的内容和位置(内存),并确定在任何特定的时间谁可以拜访高管、会面时间有多长。

为什么要这么划分呢?

系统执行的代码通过以下两种模式之一在 CPU 上运行:内核模式或用户模式。在内核模式下运行的代码可以不受限制地访问硬件,在 CPU 的所有指令中,有些指令是非常危险的,如果错用,将导致系统崩溃,比如清内存、设置时钟等。如果允许所有的程序都可以使用这些指令,那么系统崩溃的概率将大大增加。如果留给程序员的话也会大大加大编程和环境的复杂度,这样设计使得硬件资源堆程序猿来说屏蔽了。而用户模式则会限制 SCI 对 CPU 和内存的访问。内存也存在类似的分隔情况(内核空间和用户空间)。这两个小细节构成了一些复杂操作的基础,例如安全防护、构建容器和虚拟机的权限分隔。这也意味着:如果进程在用户模式下失败,则损失有限,无伤大雅,可以由内核进行修复。另一方面,由于内核进程要访问内存和处理器,因此内核进程的崩溃可能会引起整个系统的崩溃。由于用户进程之间会有适当的保护措施和权限要求,因此一个进程的崩溃通常不会引起太多问题。对于以前的 DOS 操作系统来说,是没有内核空间、用户空间以及内核态、用户态这些概念的。可以认为所有的代码都是运行在内核态的,因而用户编写的应用程序代码可以很容易的让操作系统崩溃掉。对于 Linux 来说,通过区分内核空间和用户空间的设计,隔离了操作系统代码(操作系统的代码要比应用程序的代码健壮很多)与应用程序代码。即便是单个应用程序出现错误也不会影响到操作系统的稳定性,这样其它的程序还可以正常的运行(Linux 可是个多任务系统啊!)。

所以,区分内核空间和用户空间本质上是要提高操作系统的稳定性及可用性。

如何从用户空间进入内核空间

其实所有的系统资源管理都是在内核空间中完成的。比如读写磁盘文件,分配回收内存,从网络接口读写数据等等。我们的应用程序是无法直接进行这样的操作的。但是我们可以通过内核提供的接口来完成这样的任务。

比如应用程序要读取磁盘上的一个文件,它可以向内核发起一个 "系统调用" 告诉内核:"我要读取磁盘上的某某文件"。

这时需要一个这样的机制: 用户态程序切换到内核态, 但是不能控制在内核态中执行的指令。这种机制叫系统调用, 在CPU中的实现称之为陷阱指令(Trap Instruction)

他们的工作流程如下:

用户态程序将一些数据值放在寄存器中, 或者使用参数创建一个堆栈(stack frame), 以此表明需要操作系统提供的服务.用户态程序执行陷阱指令,CPU切换到内核态, 并跳到位于内存指定位置的指令, 这些指令是操作系统的一部分, 他们具有内存保护, 不可被用户态程序访问。这些指令称之为陷阱(trap)或者系统调用处理器(system call handler). 他们会读取程序放入内存的数据参数, 并执行程序请求的服务,系统调用完成后, 操作系统会重置CPU为用户态并返回系统调用的结果。CPU的使命就是执行程序中的指令,而且CPU内部有很多用于存放数据的寄存器,其中比较重要的一个寄存器叫EIP寄存器,它用于存储下一条要执行的指令。除了EIP寄存器之外,还有一个比较重要的寄存器叫ESP寄存器,它用于保存程序的栈顶位置。除此之外,CPU还有很多其他用途的寄存器,如:通用寄存器EAX、EDX和段寄存器CS、DS等等。寄存器知识:http://www.360doc.com/content/19/1205/13/277688_877598997.shtml。当一个程序被执行(称为进程)的时候,这些寄存器的值通常会被修改。所以当要切换进程执行的时候,只需要把这些寄存器的值保存下来,然后把新进程寄存器的值赋值到CPU中,那么就完成进程切换了,通常我们把这个过程称为上下文切换。 其实际含义是任务切换, 或者CPU寄存器切换当多任务内核决定运行另外的任务时, 它保存正在运行任务的当前状态, 也就是CPU寄存器中的全部内容。这些内容被保存在任务自己的堆栈中, 入栈工作完成后就把下一个将要运行的任务的当前状况从该任务的栈中重新装入CPU寄存器, 并开始下一个任务的运行, 这一过程就是context switch。

对于一个进程来讲,从用户空间进入内核空间并最终返回到用户空间,这个过程是十分复杂的。举个例子,比如我们经常接触的概念 "堆栈",其实进程在内核态和用户态各有一个堆栈。运行在用户空间时进程使用的是用户空间中的堆栈,而运行在内核空间时,进程使用的是内核空间中的堆栈。所以说,Linux 中每个进程有两个栈,分别用于用户态和内核态。

下图简明的描述了用户态与内核态之间的转换:

那么从用户态进程到内核态进程是怎么发生的?

  • 用户线程
    • 由应用程序创建、调度、撤销,不需要内核的支持(内核不感知)
    • 由于不需要内核的支持,便不涉及用户态/内核态的切换,消耗的资源较少,速度也较快
    • 由于需要应用程序控制线程的轮换调度,当有一个用户线程被阻塞时,整个所属进程便会被阻塞,同时在多核处理器下只能在一个核内分时复用,不能充分利用多核优势
  • 内核线程
    • 由内核创建、调用、撤销,并由内核维护线程的上下文信息及线程切换
    • 由于内核线程由内核进行维护,当一个内核线程被阻塞时,不会影响其他线程的正常运行,并且多核处理器下,一个进程内的多个线程可以充分利用多核的优势同时执行
    • 由于需要内核进行维护,在线程创建、切换过程中便会涉及用户态/内核态的切换,增加系统消耗

核心是什么?是内核实现帮助我们实现了多核的分时复用的调度算法

在linux操作系统中,往往都是通过fork函数创建一个子进程来代表内核中的线程(用户线程陷入内核态其实就是在内核里面使用fork系统调用创建了一个子线程),在fork完一个子进程后,还需要将父进程中大部分的上下文信息复制到子进程中,消耗大量cpu时间用来初始化内存空间,产生大量冗余数据。为了避免上述情况,轻量级进程(Light Weight Process, LWP)便出现了,其使用clone系统调用创建子进程,过程中只将部分父进程数据进行复制,没有被复制的资源可以通过指针进行数据共享,这样一来LWP的运行单元更小、运行速度更快。当然这取决于线程模型是怎样的可以使用fork也可以使用clone。可参考此文:在linux操作系统中,往往都是通过fork函数创建一个子进程来代表内核中的线程,在fork完一个子进程后,还需要将父进程中大部分的上下文信息复制到子进程中,消耗大量cpu时间用来初始化内存空间,产生大量冗余数据。关于fork系统调用https://www.cnblogs.com/cccc2019fzs/p/13110431.html

java新起一个线程就是用的clone命令:关于java的线程和操作系统线程的关系:https://www.yuque.com/cdsnow/blog/hd1cz8

fork(进程)或者clone(线程)的过程中具体切换了什么内容呢?

一个进程的上下文可以分为三个部分:用户级上下文、寄存器上下文以及系统级上下文。

进程上下文是进程执行活动全过程的静态描述。我们把已执行过的进程指令和数据在相关寄存器与堆栈中的内容称为进程上文,把正在执行的指令和数据在寄存器与堆栈中的内容称为进程正文,把待执行的指令和数据在寄存器与堆栈中的内容称为进程下文。

用户级上下文:正文、数据、用户栈以及共享存储区;

寄存器上下文:程序寄存器(IP),即CPU将执行的下条指令地址,处理机状态寄存器(EFLAGS),栈指针,通用寄存器;

系统级上下文:进程表项(proc结构)和U区,在Linux中这两个部分被合成task_struct,区表及页表(mm_struct , vm_area_struct, pgd, pmd, pte等),核心栈等。

全部的上下文信息组成了一个进程的运行环境。当发生进程调度时,必须对全部上下文信息进行切换,新调度的进程才能运行。进程就是上下文的集合的一个抽象概念。

一般进程切换分两步
1.切换页目录以使用新的地址空间
2.切换内核栈和硬件上下文。

对于linux来说,线程和进程的最大区别就在于地址空间。
对于线程切换,第1步是不需要做的,第2是进程和线程切换都要做的。所以明显是进程切换代价大

线程上下文切换和进程上下问切换一个最主要的区别是线程的切换虚拟内存空间依然是相同的,但是进程切换是不同的。这两种上下文切换的处理都是通过操作系统内核来完成的。内核的这种切换过程伴随的最显著的性能损耗是将寄存器中的内容切换出。

另外一个隐藏的损耗是上下文的切换会扰乱处理器的缓存机制。简单的说,一旦去切换上下文,处理器中所有已经缓存的内存地址一瞬间都作废了。还有一个显著的区别是当你改变虚拟内存空间的时候,处理的页表缓冲(processor’s Translation Lookaside Buffer (TLB))或者相当的神马东西会被全部刷新,这将导致内存的访问在一段时间内相当的低效。但是在线程的切换中,不会出现这个问题。

在这里插入图片描述

什么时候产生上下文切换呢?

既然用户态的进程必须切换成内核态才能使用系统的资源,那么我们接下来就看看进程一共有多少种方式可以从用户态进入到内核态。

  1. 系统调用:用户态进程主动切换到内核态的方式,用户态主动申请操作系统提供的服务程序。比如,fork就是用户态要开辟新的进程,操作系统来帮忙实现。
  2. 异常:CPU执行用户态程序时,出现了异常,如缺页异常,就会触发进入内核态。
  3. 外围设备的中断(IO中断):外围设备完成请求后,会向CPU发出中断信号,CPU会暂停执行下一条指令转而去执行与中断信号对应的操作。比如,硬盘读写完成,就会回到中断程序执行后续操作。

整体结构

接下来我们从内核空间和用户空间的角度看一看整个 Linux 系统的结构。它大体可以分为三个部分,从下往上依次为:硬件 -> 内核空间 -> 用户空间。如下图所示(此图来自互联网):

在硬件之上,内核空间中的代码控制了硬件资源的使用权,用户空间中的代码只有通过内核暴露的系统调用接口(System Call Interface)才能使用到系统中的硬件资源。其实,不光是 Linux,Windows 操作系统的设计也是大同小异。

实际上我们可以将每个处理器在任何指定时间点上的活动概括为下列三者之一:

  • 运行于用户空间,执行用户进程。
  • 运行于内核空间,处于进程上下文,代表某个特定的进程执行。
  • 运行于内核空间,处于中断上下文,与任何进程无关,处理某个特定的中断。

以上三点几乎包括所有的情况,比如当 CPU 空闲时,内核就运行一个空进程,处于进程上下文,但运行在内核空间。
说明:Linux 系统的中断服务程序不在进程的上下文中执行,它们在一个与所有进程都无关的、专门的中断上下文中执行。之所以存在一个专门的执行环境,就是为了保证中断服务程序能够在第一时间响应和处理中断请求,然后快速地退出。

这段摘自:https://www.cnblogs.com/sparkdev/p/8410350.html

参考:https://blog.csdn.net/qq_42756396/article/details/108089158?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-0&spm=1001.2101.3001.4242

https://blog.csdn.net/u013178472/article/details/81115080

https://www.cnblogs.com/cccc2019fzs/p/13110431.html

https://blog.csdn.net/vjhghjghj/article/details/105353550?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EOPENSEARCH%7Edefault-4.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EOPENSEARCH%7Edefault-4.control

https://blog.csdn.net/weixin_39816946/article/details/110395042?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-0&spm=1001.2101.3001.4242
https://blog.csdn.net/qq_42756396/article/details/108089158?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-0&spm=1001.2101.3001.4242

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

CPU分时、中断和上下文切换 的相关文章

  • AD21几个容易忘记的快捷键

    CTRL 43 D 打开透明模式 适合等长操作 CTRL 43 M 测量距离 SHIFT 43 R 推挤走线 D 43 K 打开层叠管理器 t 43 v 43 g 从板框选择铺铜 设置鼠标滑过显示高亮 取消自动闭合回路 xff0c 用于打多
  • C字符队列

    链式队列数据结构定义 typedef struct QueueStruct char value struct QueueStruct next queueStruct void QueueInit brief 链式队列初始化 void E

随机推荐

  • QT+OpenGL(1)——包含头文件

    关于智能提示建议放弃Cmake生成 xff0c 直接包含头文件 xff0c 包含方法如下 如果找不到头文件 xff0c 直接用everything搜相应文件 xff0c 再给包含进去 需要新建文件夹 xff0c 新建文件 c cpp pro
  • 2d激光雷达(rplidar_s1)与双目摄像头联合标定

    前段时间由于项目需要使用摄像头 xff08 realsense d435i xff09 与单线激光雷达进行融合 xff0c 于是就对这两个传感器进行了标定 xff0c 使用的是CamLaserCalibraTool xff0c 这是别人开源
  • ubuntu生成&设置core文件,调试段错误

    在用ubuntu系统编码c 43 43 程序时 xff0c 经常遇到段错误 xff0c 以下介绍ubuntu系统下如何生成core文件 xff0c 设置core文件路径 xff0c 并进行调试core文件 xff0c 找到段错误原因 1 允
  • ECharts仪表盘设置主题文字颜色

    设置仪表盘中显示字体样式 xff1a 方式如下 xff1a eChartA setOption tooltip formatter 34 a lt br gt b c 34 toolbox show true feature mark sh
  • 【LWC】Resource not found异常的一个可能原因

    Resource not found异常的一个可能原因 场景 在我当前项目中 xff0c 有一个用来创建记录的LWC组件 创建记录的流程大致为以下三步 xff1a 用户输入信息用户点击保存 xff0c 后台处理数据 xff0c 创建记录记录
  • 使用apt-get update命令卡在waiting for headers

    今天 xff0c 想在自己的debian系统里面装下java8 xff0c 添加了一个源update的时候出现的点问题就ctrl c了 没想到之后再update就不行了 一直卡在100 正在读报头waiting for headers 网上
  • 四轴飞行器基本组成及其飞行原理详解

    近日 xff0c 自己组装了一台 四轴飞行器 xff1b 组装完后 xff0c 便想深究其原理 xff1b 避免只是 知其然 xff0c 却不知其所以然 xff1b 查阅资料后 xff0c 便在其他文章的基础上 xff0c 将此文 归纳整理
  • ubuntu20.04分区方案 for deeplearning

    一共分出4个系统分区 1 设置efi引导 因为是u盘的uefi启动 xff0c 因此设置一个efi引导项 具体参数 xff1a 大小 500到1024mb即可 xff08 视自身的存储空间而定 xff09 新分区的类型 xff1a 逻辑分区
  • Linux重定向和管道符

    Linux重定向和管道符 1 Uid gid是什么 xff1f 2 linux中设置环境变量的几种方法3 管道命令符和通配符4 输入输出重定向 1 Uid gid是什么 xff1f 1 1 用户组 UID以及GID概念 用户组 xff1a
  • OpenFlow概念学习

    前言 OpenFlow交换机将原来完全由交换机 路由器控制的报文转发过程转化为由OpenFlow 交换机和控制服务器来共同完成 xff0c 目的交换机要通过of协议 xff08 OpenFlow Protocol xff09 经 安全通道
  • Ubuntu创建用户(组)与权限管理

    Ubuntu创建用户 xff08 组 xff09 与权限管理 创建用户与用户组创建和删除用户及用户组给用户配置sudo权限用户管理相关的命令 创建用户与用户组 在管理服务器时 xff0c 需要注意用户权限分配 xff0c 这样不会造成重大的
  • docker修改容器与宿主机端口映射

    1 查看容器id docker ps 2 进入容器安装目录 cd var lib docker containers 找到容器对应的文件夹 xff0c 容器id与文件夹前面的id是一样的 进入文件夹 cd 593f0dd680d77e901
  • Prometheus源码学习(1) 编译源码

    代码里面看不明白的变量或者函数可以通过两种方式观测它的值来了解其含义 一种是把代码片段摘出来 xff0c 写到一个测试程序里运行一下另一种时日志里打印它的值来观察 第一种比较简单易行 xff0c 但是代码片段要比较独立才好做 xff0c 第
  • ubuntu的命令&操作

    记录ubuntu系统使用中一些常用的操作 1 在ubuntu终端打开图像界面的文件夹 xff1a cd到指定的目录之后执行nautilus即可 参考 xff1a linux系统ubuntu中在命令行如何打开图形界面的文件夹 李照耀 博客园
  • Leetcode解题目录(Python版)

    Leetcode解题目录 xff08 Python版 xff09 题目目录1 排序算法2 哈希表3 动态规划 题目来源于LeetCode官网题库 xff0c 解题思路参考官网各大佬 xff0c 这里做一个目录方便大家查找 xff0c 另外方
  • error while loading shared libraries: libopencv_imgcorecs.so.3.4:: cannot open shared object file:

    ubuntu16 04的环境下 xff0c 编译成功C 43 43 代码 xff0c 同时也安装好了opencv3 4 在运行时却出现error xff1a error while loading shared libraries libo
  • 基于LQR的车辆LKA算法设计

    记录分享一下基于LQR控制算法的车辆LKA设计 通过carsim和simulink进行联合仿真 1控制算法设计 整体思路是通过车辆以及道路模型 得到控制器的输入 v 纵向速度 r 横摆角速度 y 侧向路径偏移 phi 横摆角误差 通过最优控
  • 软件框架

    1 概念 框架 xff08 framework xff09 是一个框子 指其约束性 xff0c 也是一个架子 指其支撑性 是一个基本概念上的结构 xff0c 用于去解决或者处理复杂的问题 框架这个广泛的定义使用的十分流行 xff0c 尤其在
  • 使用C++开发STM32 FreeRTOS工程与添加DSP库

    续上一篇文章的内容 由CubeMX构建的Makefile工程只支持C和汇编的编译 xff0c 而且FreeRTOS的代码也需要作为C代码编译 当我们想使用C 43 43 时 xff0c 需要做一些准备并且修改Makefile xff1b 另
  • CPU分时、中断和上下文切换

    准备知识 xff1a 实时和分时 嵌入式操作系统可以分为实时操作系统和分时操作系统两类 我们现实之中使用的绝大多数是分时操作系统 xff0c 比如windows或者linux 但是比如汽车就必须使用实时操作系统 xff0c 举一个经常使用的