【Linux】:进程信号(详谈信号捕捉 OS 运行)

news2024/11/18 10:32:12

✨                                                 来去都是自由风,该相逢的人总会相逢      🌏 

📃个人主页:island1314  

🔥个人专栏:Linux—登神长阶

⛺️ 欢迎关注:👍点赞 👂🏽留言 😍收藏  💞 💞 💞


1. 信号捕捉的流程 💦

前情回顾 + 知识补充:

 ​​​​​​​​​​​​​​ 还记得我们之前说过的吗,处理信号时并不一定立即去处理,有可能此时我们在做一个优先级很高的事情, 此时信号处理就会等到一个合适的时候去处理。

现在我们就要来理解一下,什么叫作合适的时候 ❓

  • 也就是 进程在从 内核态 切换回 内核态 的时候,检测当前进程的 pending && block,决定是否处理 handler 表处理信号 !
    • 内核态:我自己写的代码
    • 内核态:执行操作系统的代码
    • 两者都属于操作系统运行状态

言归正传,让我们开始讲信号捕捉的具体流程

如果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为捕捉信号

由于信号处理函数的代码是在用户空间的,处理过程比较复杂,举例如下;

  • 用户程序注册了 SIGQUIT 信号的处理函数 sighandler
  • 当前正在执行 main 函数,这时发生中断或异常切换到内核态
  • 在中断处理完毕后要返回用户态的 main 函数之前检査到有信号 SIGQUIT 递达:
  • 内核决定返回用户态后不是恢复 main 函数的上下文继续执行,而是执行 sighandler函数,sighandlermain 函数使用不同的堆栈空间,它们之间不存在调用和被调用的关系,是两个独立的控制流程。
  • sighandler 函数返回后自动执行特殊的系统调用 sigreturn 再次进入内核态。
  • 如果没有新的信号要递达,这次再返回用户态就是恢复 main 函数的上下文继续执行了。

2. 操作系统是怎么运行的 📚

2.1 🍉 硬件中断

🔥 硬件中断(Hardware Interrupts):操作系统在运行时依赖硬件中断来响应外部事件,如输入设备(鼠标、键盘)、网络接口卡或硬盘控制器等硬件设备的请求。当外部设备需要操作系统的注意时,硬件会触发中断,这时CPU会暂停当前的执行,转而执行操作系统的中断处理程序。通过这种机制,操作系统实际上被硬件所“推动”来执行某些任务。

  • 中断向量表就是操作系统的一部分,启动就加载到内存中了
  • 通过外部硬件中断,操作系统就不需要对外设进行任何周期性的检测或者轮询
  • 由外部设备触发的,中断系统运行流程,叫做硬件中断

CPU 现场保护解释:

  • CPU 现场保护(CPU Context Save)是指在操作系统中,当进程切换或中断发生时,操作系统需要保存当前正在执行的进程或线程的状态,以便在之后能够恢复到中断或切换之前的状态。

  • 现场保护确保了进程之间的切换或中断处理的正确性,使得每个进程能够在被暂停后,继续从它中断的位置继续执行,而不会丢失任何上下文信息。

现场保护过程

  1. 保存现场

    • 当操作系统发生中断(例如时钟中断、I/O中断)或进程切换时,操作系统会先保存当前进程的寄存器内容、程序计数器等现场信息到进程的内存结构中(通常是进程的内核栈或调度表中)。
    • 同时,操作系统会记录当前的内存管理信息,以便进程能够在恢复时继续使用正确的虚拟内存空间。
  2. 中断处理

    • 保存现场后,操作系统会根据中断的类型进行相应的处理。例如,执行I/O操作、中断处理程序、或者进行系统调用等。
  3. 恢复现场

    • 当操作系统完成中断处理或进程调度后,它会根据先前保存的现场信息恢复进程的状态。操作系统会恢复程序计数器、寄存器和其他硬件状态,使得进程能够继续执行。
    • 如果是进程切换,操作系统还会切换到目标进程的内存空间,并重新加载目标进程的上下文。

2.2 🍅 时针中断

问题:

  1. 进程可以在操作系统的指挥下,被调度,被执行,那么操作系统自己被谁指挥,被谁推动执行呢?
  2. 外部设备可以触发硬件中断,但是这个是需要用户或者设备自己触发,有没有自己可以定期触发的设备?

概念补充:

  • 时针源(Clock Source)是指用来驱动时钟系统的信号源,在电子设备和计算机系统中,时针源提供了一个稳定的、周期性的信号,通常称为时钟信号(Clock Signal)
    • 时针源对于数字电路和处理器的正常运作至关重要,因为它确定了系统中各个部分的操作节奏和同步方式
    • 时针源不仅提供时钟信号,还负责系统中多个部件之间的同步,比如:在数字电路中,所有的操作都需要依赖时钟脉冲进行同步
  • 主频(也称为时钟频率)是衡量计算机中央处理单元(CPU)处理速度的一个重要指标。
    • 表示CPU每秒钟能够执行的时钟周期次数,通常以赫兹(Hz)为单位,1赫兹等于每秒钟一个周期。
    • 主频由时钟源提供,决定了CPU每秒钟能完成多少次运算操作。主频越高,CPU的计算速度越快,理论上每秒可以处理更多的指令
  • 进程调度不一定是切换
    • 操作系统进行调度决策时,并不总是需要切换正在执行的进程
    • 调度可能仅仅是对现有进程的重新评估、更新优先级、或决定是否切换到另一个进程
    • 在一些情况下,进程调度发生时,当前进程可能仍然继续运行,不需要进行实际的进程切换
    • 本质:让当前进程的时间片进行 --,每个进程执行一段时间后,操作系统会中断其执行并切换到下一个进程,直到进程的时间片耗尽,计数变为0就开始进行切换

此时,我们就发现 操作系统不就在 硬件的推动下自动调度了嘛

2.3 🍒 死循环

如果是这样,操作系统不就可以躺平了吗?对,操作系统自己不做任何事情,需要什么功能,就向中断向量表里面添加方法即可。

  • 操作系统的本质:就是一个死循环!
void main(void)/*这里确实是void,并没错。*/
{            /*在startup 程序(head.s)中就是这样假设的 */
/*
*    注意!!对于任何其它的任务,'pause()'将意味着我们必须等待收到一个信号才会返回就绪运行态,
*    但任务0(task0)是唯一的意外情况(参见'schedule()'),因为任务0 在任何空闲时间里
*    都会被激活(当没有其它任务在运行时),
*    因此对于任务8'pause()'仅意味着我们返回来查看是否有其它任务可以运行,如果没
*    有的话我们就回到这里,一直循环执行 "pause()"
*/
    for(;;)
        pause();
}
  • 结论:这样,操作系统,就可以在硬件时钟的推动下,自动调度了

所以,什么是时间片?CPU为什么会有主频?为什么主频越快,CPU越快?

  • 🍈时间片是操作系统中 用于实现任务调度的一个概念,本质就是 PCB 内部的一个计数器

    • 它指的是分配给每个进程或线程的一小段时间,通常为毫秒级别

    • 在时间片内,CPU会执行当前进程的指令,当时间片用完时,操作系统会暂停当前进程,切换到下一个进程或线程

    • 这种机制用于实现多任务并发,确保多个进程能够共享CPU时间

  • 🍈主频的存在是为了确保系统内所有操作的同步性

    • 在一个复杂的计算机系统中,CPU需要与内存、外设等其他组件协调工作,而所有的任务必须按照一定的节奏进行

    • 时钟脉冲为这些协调提供了节奏基准。每次时钟信号的变化(通常是上升沿或下降沿)会触发CPU执行特定的操作,主频的高低决定了CPU每秒可以执行多少个这样的操作

  • 🍈主频越快,CPU越快的原因:CPU执行每个指令时都依赖于时钟脉冲进行同步

    • 每个时钟周期内,CPU能完成一定数量的操作。

    • 主频越高,每秒钟的时钟周期数越多,因此CPU能在单位时间内执行更多的指令,响应更快、处理任务更高效。

    • 但也要注意,主频并不是唯一决定CPU性能的因素,架构设计、核心数、缓存等也同样影响最终的计算能力。

对于时间片这里补充一个知识

时间片的更新机制:

💦 时间片的更新和进程的调度通常依赖于 硬件中断(尤其是 时钟中断)。每当时钟中断触发时,操作系统会检查当前进程是否已经使用完时间片,并根据调度算法来切换进程。这个机制确保了操作系统能够高效地进行多任务处理,实现进程的公平调度和资源共享。

具体过程:

  1. 时钟中断的触发: 操作系统通过定时器硬件(例如,系统时钟芯片)来生成定期的时钟中断。每当时钟计时器达到预定时间间隔(如10毫秒),就会产生一次中断,CPU会暂停当前执行的程序,转而执行中断处理程序。

  2. 操作系统的中断处理: 当时钟中断发生时,操作系统会进行中断处理。中断处理程序的主要任务之一就是更新进程的时间片。此时,操作系统会检查当前进程是否已经用完它的时间片。如果时间片已经用尽,操作系统会选择一个新的进程(根据调度算法,如时间片轮转、优先级调度等)并将控制权转移给该进程。

  3. 时间片轮转(Round Robin)调度: 在使用 时间片轮转(Round Robin) 调度算法时,每个进程被分配一个固定长度的时间片(例如10毫秒)。每当时钟中断发生时,操作系统会:

    • 检查当前进程是否已经用完时间片。
    • 如果用完,则将当前进程挂起(保存上下文),并将其放回就绪队列。
    • 选择下一个进程来执行,并恢复该进程的上下文。

如果当前进程还没有用完时间片,操作系统通常会继续执行该进程,直到下一个时钟中断

2.4 🍎 软件中断

前情回顾:

  • 上述外部硬件中断,需要硬件设备触发。
  • 有没有可能,因为软件原因,也触发上面的逻辑?有!
  • 为了让操作系统支持进行系统调用,CPU也设计了对应的汇编指令(int 或者 syscal),可以让CPU内部触发中断逻辑。

🔥 软件中断(Software Interrupt)是一种由程序或操作系统主动触发的中断机制,用于实现进程间的通信、系统调用、异常处理等功能。与硬件中断不同,硬件中断是由外部设备(如输入设备、网络接口卡等)触发的,而软件中断则是程序内部通过特定的指令或操作触发的中断。

问题:

  • 用户层怎么把系统调用号给操作系统? ---> 寄存器(比如EAX)
  • 操作系统怎么把返回值给用户? ---> 寄存器或者用户传入的缓冲区地址
  • 系统调用的过程,其实就是先int 0x80、syscall陷入内核,本质就是触发软中断,CPU就会自动执行系统调用的处理方法,而这个方法会根据系统调用号,自动查表,执行对应的方法
  • 系统调用号的本质:数组下标!

可是为什么我们用的系统调用,从来没有见过什么 int 0x80 或者 syscall 呢?都是直接调用上层的函数的啊?

 那是因为 Linux 的 gnu C 标准库,给我们把几乎所有的系统调用全部封装了。

小知识:系统调用号(理解)

🍏 系统调用号(System Call Number) 是操作系统提供的用于执行内核服务的标识符。它是程序通过系统调用(System Call)请求操作系统执行特权操作的关键。每个系统调用在内核中都有一个唯一的编号,程序通过这个编号来请求对应的服务

什么是系统调用?

  • 系统调用是应用程序与操作系统内核之间的接口。用户程序无法直接访问硬件和内核功能,因此它们通过系统调用来请求操作系统提供的服务。例如,文件操作、进程控制、内存管理、设备控制等都需要通过系统调用来完成。

系统调用号的工作原理

  1. 应用程序发起请求:应用程序(用户态)通过执行一个特定的指令(如 int 0x80syscall)来请求内核服务。系统调用号通常是通过寄存器传递给操作系统的。

  2. 系统调用号传递:系统调用号告诉内核要执行哪个具体的操作。例如,read 系统调用对应的系统调用号是一个固定值。当程序发起系统调用时,它会将该系统调用号存入一个特定的寄存器。

  3. 内核处理请求:操作系统内核根据系统调用号确定对应的服务,并执行相应的操作。执行完成后,结果会通过返回值或者寄存器传递给用户程序。

① 在早期的 x86 架构中,程序通过 int 0x80 中断来触发系统调用,系统调用号通过 eax 寄存器传递

② 在现代的 64 位架构中(如 x86_64),通常使用 syscall 指令,并通过寄存器传递系统调用号和参数

在 Linux 系统中,系统调用号的映射通常是通过一个系统调用表来完成的

  • 每个系统调用号对应一个内核函数指针(即内核中某个具体的处理程序的地址)
  • 系统调用号是由操作系统预先定义的,用户程序通过标准库(如 libc)提供的接口来进行系统调用,系统库再根据系统调用号通过内核接口将请求传递给操作系统

2.5 🍓 缺页中断? 内存碎片处理?除零野指针错误❓

我们先来了解一下缺页中断、内存碎片处理、以及除零/野指针错误的概念以及四者究竟和 软件系统有什么练习呢?

1)缺页中断(Page Fault)

🔥 缺页中断 是当程序访问的某个虚拟内存页面不在物理内存中时,操作系统触发的一种 软件中断。操作系统需要处理缺页中断,以将缺失的内存页面从磁盘或其他存储设备加载到物理内存中。这个过程对于虚拟内存管理是至关重要的。

如何发生:

  • 当程序尝试访问某个页面时,若该页面不在物理内存中(即页面不在内存的缓存中),会产生缺页中断。
  • CPU 会触发一个 软件中断,操作系统接收到该中断后,执行缺页中断处理程序,加载缺失的页面到内存。

关系:

🎈 缺页中断 是软件中断的一个典型应用,操作系统通过捕获 软件中断 来实现虚拟内存机制,从而扩展内存容量并有效管理内存资源。

2)内存碎片处理

🔥内存碎片问题 通常分为 外部碎片(内存中存在空闲的、不连续的块)和 内部碎片(已分配的内存块中有未使用的部分)。虽然内存碎片的管理本身并不是由软件中断直接处理,但操作系统的内存管理模块可能会使用软件中断来触发内存的回收、整理和分配。

内存管理中的中断:

  • 当操作系统需要执行内存分配或回收操作时,它可能会通过软件中断通知内存管理模块。
  • 在某些情况下,例如在内存分配失败或系统内存不足时,操作系统可能会触发一个中断(通过调用页表管理或其他内存调度机制),执行 内存压缩 或 内存回收

关系:

🎈内存碎片的管理涉及操作系统的内存分配策略,操作系统通常在执行内存分配时监控和处理碎片问题,可能会通过 软件中断 来触发特殊的内存整理操作(比如垃圾回收、内存回收、页表更新等)。

3)除零错误

🔥除零错误发生在程序尝试进行除法运算时,如果除数为零,CPU 会检测到这个异常情况并触发一个 软件中断 。这种错误通常由操作系统或硬件异常机制进行处理。

如何发生:

  • 如果程序执行一个除法操作,若除数为零,CPU 会检测到此异常并产生一个除零错误中断。
  • 该中断被传递给操作系统或处理器内核,操作系统会进行异常处理(例如,终止程序或返回错误代码)。

关系:

🎈除零错误是一个典型的 软件中断,它触发了一个硬件异常,然后通过操作系统的异常处理机制(通常是通过软件中断的形式)进行处理。操作系统可能会捕获这个异常并采取适当的应对措施(如终止程序或抛出异常)。

4)野指针错误

🔥野指针错误通常发生在程序访问已经释放或未初始化的指针时,这种错误会导致程序访问非法内存,可能会引起 段错误(Segmentation Fault) 或其他内存错误。这些错误通常是通过 软件中断 来捕获和处理的。

如何发生:

  • 如果程序使用了一个无效的指针(例如,指向已释放的内存或未初始化的内存),CPU 会检测到该内存访问异常,并通过硬件产生一个 段错误 或 内存访问违规 中断。
  • 操作系统通过捕获这些中断,通常会终止程序,防止其进一步破坏内存空间。

关系:

🎈 野指针错误是由程序的内存访问违规引起的,而这种违规通常会触发硬件异常(例如 段错误),通过 软件中断 由操作系统捕获和处理。操作系统会通过 异常处理机制 来应对这种错误,避免程序继续运行下去。


从上面我们可以发现:这些问题(缺页中断、内存碎片、除零错误和野指针错误)都涉及到操作系统对硬件或程序异常情况的处理,而这些异常处理是通过 软件中断 来实现的。

  1. 缺页中断:操作系统使用时钟中断等机制捕获并处理虚拟内存中的缺页错误。
  2. 内存碎片:操作系统可能通过定期的内存管理操作和中断机制来处理内存碎片。
  3. 除零错误:CPU检测到除数为零时,通过中断通知操作系统进行错误处理。
  4. 野指针错误:程序访问非法内存时,CPU触发异常,操作系统通过软件中断捕获并进行错误处理。
  • 软件中断是操作系统管理和控制这些异常事件的基础,确保系统能够正常运行并对各种错误进行适当的响应和处理。

中断向量表如下:

看了这么多了,相信大家对操作系统如何运行的,也有个大概了解,结论如下:

  • 操作系统就是躺在中断处理例程上的代码块!我们刚刚所说的中断处理例程就是 操作系统
  • CPU 内部的软件中断,比如 int 0x80 或者 syscall,我们叫作 陷阱
  • CPU 内部的软件中断,比如 除零/ 野指针 等,我们叫作 异常。(因此,就能理解  "缺页中断"  为什么会这样叫了嘛?)
  • 因此学到这,我们其实可以 "狭隘地" 认为 操作系统 就是 中断

我们上面谈的都是内核,接下来要把我们的进程和刚刚讲到知识进行整合,另外还需要理解 操作系统不也是死循环嘛,既然它 是 一个中断,那其需不需要 调度呢 ?

3. 如何理解内核态与用户态 🖊

对上图的 知识补充如下:

🥎 内核态与用户态:

  • 内核态: 0-4G 范围的虚拟空间地址都可以操作,尤其是对 3-4G 范围的高位虚拟空间地址必须由内核态去操作
  • 用户态:3G - 4G 部分大家是共享的(指所有进程的内核态逻辑地址是共享同⼀块内存地址),是内核态的地址空间,这⾥存放在整个内核的代码和所有的内核模块,以及内核所维护的数据
1. 虚拟内存和页表的基本概念
  • 虚拟内存:虚拟内存使得程序可以拥有一个连续的内存地址空间,而不必关心物理内存的实际布局。操作系统通过页表将虚拟地址映射到物理内存地址。

  • 页表:页表是操作系统管理虚拟地址到物理地址映射的一个数据结构。每个进程在运行时通常都有自己的页表,将其虚拟内存地址映射到物理内存。

2. 内核空间与用户空间的划分

现代操作系统通常将虚拟内存划分为两个主要区域:

  • 用户空间:用于运行用户应用程序的内存区域。
  • 内核空间:用于操作系统内核代码和内核数据的内存区域。

通常,用户空间和内核空间是分开的,这使得用户程序不能直接访问内核代码和数据,从而提高了系统的安全性和稳定性。

3. 内核区和用户区的页表管理

🍏 对于大多数现代操作系统,内核区的页表和用户区的页表是由操作系统分别管理的,但在一些情况下,它们可能会共享部分内容。具体来说,内核如何管理这两者的页表,取决于是否使用分离的页表共享页表

3.1 分离的页表

💧 在一些系统中,用户空间和内核空间各自有独立的页表。操作系统在切换用户进程和内核模式时,会根据需要切换不同的页表。

  • 用户模式下,CPU 会使用映射到用户空间的页表。
  • 内核模式下,操作系统切换到映射到内核空间的页表。内核代码和数据会有不同的虚拟地址空间映射。

例如,在 x86 架构的操作系统中,x86-64 的虚拟地址通常分为两部分:低地址部分用于用户空间,高地址部分用于内核空间。每当发生上下文切换时,操作系统会根据当前是用户模式还是内核模式来切换页表,确保用户程序无法访问内核内存。

3.2 共享页表

在某些情况下,操作系统为了提高效率,可能会选择让用户空间和内核空间共享一部分页表。也就是说,用户空间的页表会映射一部分内核空间的地址,通常是内核空间中的部分内存区域。这种做法常见于一些设计优化,尤其是对于内核空间和用户空间的边界比较接近的架构。

  • 例如,在一些系统中,内核空间的映射可能被放置在虚拟地址的高端,用户程序的虚拟地址空间的上半部分(靠近高地址的部分)和内核的虚拟地址空间共享一些页表映射。这样可以在用户模式和内核模式之间切换时,减少页表切换的开销。
3.3 操作系统如何处理切换

当进程切换(例如从用户态切换到内核态,或从一个进程切换到另一个进程)时,操作系统需要切换页表。以下是内核和用户区页表切换的一些关键概念:

  • 内核页表与用户页表的映射:在切换到内核模式时,内核页表会映射一部分内核代码和数据的地址,而当系统从内核返回用户空间时,使用的是用户进程的页表。

  • 上下文切换:操作系统在切换上下文时,会保存和加载进程的页表指针。在 x86 架构中,内核有时会通过虚拟地址映射分页机制来避免频繁的页表切换,从而提高效率。

3.4 内核页表和用户页表如何协作

🎈 有时候,操作系统可能会利用一种叫做用户空间与内核空间共享页表的机制,这种方式使得在用户程序需要调用内核服务时,减少了切换的复杂性。例如,在 Linux 操作系统中,通过启用某些功能(如页表共享)来加速内核与用户空间之间的切换。

  • 共享内存:用户空间和内核空间共享一部分页表时,可以通过共享内存区域实现。某些内存区域可能会被映射到内核和用户的虚拟地址空间,允许用户空间和内核空间之间的直接数据交换。

  • 虚拟内存分段:操作系统可能将高地址部分的虚拟内存区域分配给内核代码和数据,而低地址部分分配给用户程序。在这种情况下,用户和内核可能使用同一页表,但是通过使用不同的虚拟地址空间,保护了内核内存不被用户程序直接访问。

⚾ CPL 是什么(理解)

🔥 CPL(Current Privilege Level)是一个与计算机体系结构、特别是与处理器的权限级别(特权级别)相关的概念。它用于描述当前执行代码的特权级别,在不同的特权级别下,程序对系统资源和硬件的访问权限是不同的。

1. CPL 与特权级别

在现代计算机体系结构中,特别是基于 x86 架构的处理器,操作系统通过特权级别来控制对硬件资源和内核空间的访问。特权级别通常用4个等级表示,编号从 0 到 3,CPL 表示当前代码执行时的特权级别。每个特权级别具有不同的权限:

  • CPL 0(Ring 0):内核模式(也称为特权模式)

    • 这是最高的特权级别,操作系统内核的代码通常运行在这个级别。具有对所有硬件资源和内核数据结构的完全访问权限。可以执行任意指令,包括对受保护硬件的访问。
  • CPL 1(Ring 1):驱动程序模式

    • 这是一个中间级别,通常用于设备驱动程序。驱动程序需要访问硬件,但并不需要完全的内核特权,因此运行在这个级别的代码可以访问部分受保护的硬件资源,但不能执行内核模式下的特权操作。
  • CPL 2(Ring 2):用户模式

    • CPL 2 不是一个常见的级别,它存在于一些架构中,但很少使用。类似于 CPL 1,用于一些特殊的操作系统功能,通常在这种级别运行的代码可以访问一部分硬件资源,但并没有完全的控制权。
  • CPL 3(Ring 3):用户模式

    • 这是最低的特权级别,也是用户程序和应用程序的默认运行级别。它限制了程序对系统资源的访问,以避免对操作系统或其他程序产生破坏。大多数应用程序在这一层级运行。
2. CPL 的作用

💢CPL 的作用主要是保护系统免受恶意或错误代码的影响。通过区分不同的特权级别,操作系统能够有效地限制不同级别代码的权限,确保内核模式(Ring 0)代码具有对硬件的完全控制权,而用户模式(Ring 3)代码只能在限制条件下执行,从而减少了潜在的安全风险。

  • 安全性:通过将用户程序与内核代码隔离,可以防止用户程序直接访问和修改内核空间的数据结构或执行特权指令,从而提高系统的安全性和稳定性。

  • 稳定性:当用户程序发生错误时,它不会直接影响到内核或其他重要系统资源。操作系统可以通过一些机制,如上下文切换和保护模式,确保内核空间代码不受干扰。

3. CPL 与特权指令

🔥 操作系统通过特权指令控制对不同硬件资源的访问。特权指令只有在较高的特权级别下(例如 CPL 0)才能执行。当系统调用发生时,CPU 会从较低的特权级别(如 CPL 3)切换到较高的特权级别(如 CPL 0),使得操作系统能够执行需要更高权限的操作,如访问硬件、管理内存等。

  • 特权指令:例如控制硬件中断、访问 IO 端口、修改内存管理相关的寄存器等。
  • 非特权指令:应用程序通常只能执行非特权指令,如算术运算、基本的内存读写等。
4. CPL 与栈

在 x86 架构中,CPL 通常通过栈段(Stack Segment)来管理。当发生系统调用或中断时,处理器会检查当前的 CPL,并根据需要调整栈段和 CPU 的状态以确保系统的安全性。例如,系统调用通常会导致栈从用户模式(CPL 3)切换到内核模式(CPL 0),从而可以访问内核资源。

5. CPL 和系统调用

当用户程序通过系统调用请求操作系统服务时,它会触发特权操作,导致 CPL 由 3(用户模式)变为 0(内核模式)。操作系统处理完请求后,通常会将 CPL 恢复为 3,继续执行用户程序。

​​​​​​​⚽ 结论:

① 操作系统无论怎么切换进程,都能找到同一个操作系统!

换句话说操作系统系统调用方法的执行,是在进程的地址空间中执行的!( 理解如下:

1. 操作系统的核心与进程管理

🎐 操作系统是一个在计算机硬件之上管理资源(如CPU、内存、磁盘、网络等)的系统软件,它负责在多个进程之间切换并确保每个进程能够正确地执行。当我们说“切换进程”,实际上是指操作系统通过一种叫做“上下文切换”的机制,在运行中的不同进程之间切换CPU资源。

  • 每个进程在执行时有自己的上下文(包括程序计数器、寄存器、堆栈、内存映射等)。
  • 操作系统通过保存当前进程的上下文和加载下一个进程的上下文来进行进程切换。

这意味着,当一个进程被挂起并且另一个进程开始执行时,操作系统会记住每个进程的状态(包括它所执行的代码、数据等信息),并在需要时恢复这些状态。因此,无论有多少次进程切换,操作系统总能“找到”并恢复到正确的进程状态。

2. 操作系统的全局控制与一致性

💢 操作系统在切换进程时并没有改变 “操作系统本身” 。操作系统的内核保持不变,它通过一组管理机制(如调度器、内存管理、进程管理等)来控制所有进程的执行。虽然每次切换进程时,CPU执行的是不同的进程代码,但操作系统内核总是负责调度、管理和提供对资源的访问。

即使进程不断被切换,操作系统始终维持着:

  • 进程的调度:确保每个进程有机会执行。
  • 系统资源的分配:确保每个进程都能使用CPU、内存等资源。
  • 进程的隔离:确保一个进程的行为不会干扰到其他进程。
  • 系统状态的维护:操作系统维护所有进程的状态信息,以便在需要时恢复执行。

在进程切换的过程中,操作系统并没有改变自己。它继续作为系统的主导部分,负责对所有资源和进程进行管理和调度。因此,不管切换多少次进程,操作系统“本身”仍然是一个统一的存在,它的控制和管理能力始终在工作。

3. 内核空间与用户空间

💢 操作系统通常分为内核空间用户空间。内核空间是操作系统核心部分的执行环境,它负责进程管理、内存管理、硬件抽象等操作;用户空间是普通用户程序运行的地方。进程切换发生在内核空间中,操作系统内核负责在进程之间进行切换。

  • 在进程切换时,操作系统通过保存和恢复每个进程的上下文(如程序计数器、寄存器等),让不同的进程能够继续执行。
  • 操作系统保持对硬件和资源的控制,保证切换进程的过程对系统外部是透明的。

4. 上下文切换

💢 上下文切换是操作系统切换进程时的核心机制。每当操作系统决定将当前运行的进程换成另一个进程时,它会保存当前进程的上下文(包括寄存器的值、程序计数器等),并加载新进程的上下文。这保证了不同进程的执行状态可以在系统中被正确保存和恢复。

5. 持续性与一致性

💢 “找到同一个操作系统” 的含义也可以理解为在每次切换进程时,操作系统始终保持一致的行为和状态。操作系统内核并没有被进程的切换所改变,它在每次切换后都能恢复到一致的状态,并保持全局的控制。这种持续性确保了无论系统运行多长时间,进程切换如何频繁,操作系统都能正常工作,保持整个系统的稳定性和一致性。

② 在用户层的时候不关心系统调用地址

  • 用户层代码与操作系统内核的交互是通过一种叫做**系统调用接口(System Call Interface, SCI)**的机制进行的。这种接口抽象了系统调用的具体实现,用户程序只需要通过标准的函数接口来发起请求,而不需要关心如何与内核直接交互

【*★,°*:.☆( ̄▽ ̄)/$:*.°★* 】那么本篇到此就结束啦,如果我的这篇博客可以给你提供有益的参考和启示,可以三连支持一下 !!

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

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

相关文章

视觉SLAM相机——单目相机、双目相机、深度相机

一、单目相机 只使用一个摄像头进行SLAM的做法称为单目SLAM,这种传感器的结构特别简单,成本特别低,单目相机的数据:照片。照片本质上是拍摄某个场景在相机的成像平面上留下的一个投影。它以二维的形式记录了三维的世界。这个过程中…

MongoDB在现代Web开发中的应用

💓 博客主页:瑕疵的CSDN主页 📝 Gitee主页:瑕疵的gitee主页 ⏩ 文章专栏:《热点资讯》 MongoDB在现代Web开发中的应用 MongoDB在现代Web开发中的应用 MongoDB在现代Web开发中的应用 引言 MongoDB 概述 定义与原理 发展…

OceanBase 分区表详解

1、分区表的定义 在OceanBase数据库中,普通的表数据可以根据预设的规则被分割并存储到不同的数据区块中,同一区块的数据是在一个物理存储上。这样被分区块的表被称为分区表,而其中的每一个独立的数据区块则被称为一个分区。 如下图所示&…

Linux(CentOS 7) yum一键安装mysql8

1、通过yum安装 (1)下载mysql 在Linux找个地方输入以下命令 wget https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm (2)安装mysql yum 仓库配置文件 [rootVM-8-15-centos ~]# sudo rpm -Uvh mysql80-c…

记一次预览USB摄像头并获取实时回调数据的过程(UVCAndroid集成)

背景 主工程是gradle4.8 jdk1.8 启用jetifier要接入的usb摄像头的库是UVCAndroid gradle8.7 jdk17 接入过程 看了下setCallbackActivity非常适合我们的需求,而且回调后的数据是RGB888,看到demo中用到了xml若干于是想到用aar打包,整个过程也…

shell脚本_永久环境变量和字符串操作

一、永久环境变量 1. 常见的环境变量 2. 设置永久环境变量 3.1.将脚本加进PATH变量的目录中 3.2.添加进环境变量里 3.2.修改用户的 shell 配置文件 二、字符串操作 1. 字符串拼接 2. 字符串切片 3. 字符串查找 4. 字符串替换 5. 字符串大小写转换 6. 字符串分割 7…

操作系统进程管理实验

父子进程 用系统调用fork()函数实现子进程的创建&#xff0c;熟悉进程创建的执行过程。 #include <stdio.h> #include <stdlib.h> #include <unistd.h>int main() {// 打印主进程的 PIDprintf("hello, world (pid: %d)\n", (int)getpid());// 创…

DB Type

P位 p 1时段描述符有效&#xff0c;p 0时段描述符无效 Base Base被分成了三个部分&#xff0c;按照实际拼接即可 G位 如果G 0 说明描述符中Limit的单位是字节&#xff0c;如果是G 1 &#xff0c;那么limit的描述的单位是页也就是4kb S位 S 1 表示代码段或者数据段描…

获取当前程序运行时的栈大小[C语言]

废话前言 一晃已经毕业了4年&#xff0c;也在某个时间点&#xff0c;从面试者转变成了面试官。 进行第一次面试的时候&#xff0c;我好像比候选人还慌张&#xff0c;压根不知道问什么&#xff0c;好在是同行业&#xff0c;看着简历问了一些协议内容以及模块设计思路&#xff0…

新160个crackme - 098-DueList.4

运行分析 需破解Name和Code PE分析 ASM汇编程序&#xff0c;32位&#xff0c;无壳 静态分析&动态调试 ida搜索找到关键字符串 ida动态调试&#xff0c;逻辑如上 算法分析 Name concealstr_1 SU7CSJKF09NCSDO9SDF09SDRLVK7809S4NF str_2 A1LSK2DJF4HGP3QWO5EIR6UTYZ8MXN…

ETH钱包地址如何获取 如何购买比特币

首先我们要先注册一个交易所 Gate.io&#xff08;推荐&#xff09;: 点我注册 1、注册很简单&#xff0c;通过手机号就可以进行注册了。 2、获取ETH钱包地址 注册好之后&#xff0c;如图所示&#xff0c;点击“统一账户” 3、通过搜索栏搜索ETH&#xff0c;如下图所示 4、点…

【HOT100第五天】搜索二维矩阵 II,相交链表,反转链表,回文链表

240.搜索二维矩阵 II 编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性&#xff1a; 每行的元素从左到右升序排列。每列的元素从上到下升序排列。 先动手写写最简单方法&#xff0c;二重循环。 class Solution { public:bool searchMa…

.NET 9与C# 13革新:新数据类型与语法糖深度解析

记录&#xff08;Record&#xff09;类型 使用方式&#xff1a; public record Person(string FirstName, string LastName); 适用场景&#xff1a;当需要创建不可变的数据结构&#xff0c;且希望自动生成 GetHashCode 和 Equals 方法时。不适用场景&#xff1a;当数据结构需…

冠层四流近似模型的发展历史

1. Kunbelka-Munk theory This is the earlist model using a two-stream approximation d I d z − ( k s ) I s J d J d z ( k s ) J − s I \begin{aligned} &\frac{dI}{dz} -(ks)IsJ\\ &\frac{dJ}{dz} (ks)J - sI \end{aligned} ​dzdI​−(ks)IsJdzdJ​(…

MySQL-关联查询和子查询

目录 一、笛卡尔积 二、表连接 1、内部连接 1.1 等值连接 1.2 非等值连接 2、外部链接 2.1 左外连接-LEFT JOIN 2.2 右外连接-RIGHT JOIN 2.3 全关联-FULL JOIN/UNION 三、子查询 1、嵌套子查询 2、相关子查询 3、insert和select语句添加数据 4、update和select语…

云计算虚拟化-kvm创建虚拟机

作者介绍&#xff1a;简历上没有一个精通的运维工程师。希望大家多多关注作者&#xff0c;下面的思维导图也是预计更新的内容和当前进度(不定时更新)。 虚拟化&#xff0c;简单来说就是把一台服务器/PC电脑&#xff0c;虚拟成多台独立的虚拟机&#xff0c;每台虚拟机之间相互隔…

计算机编程中的设计模式及其在简化复杂系统设计中的应用

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 计算机编程中的设计模式及其在简化复杂系统设计中的应用 计算机编程中的设计模式及其在简化复杂系统设计中的应用 计算机编程中的…

编程考古-计算机发展(中)

晶体管计算机时代 尽管真空管技术标志着计算机步入了现代化的门槛&#xff0c;但其固有的局限性——庞大的体积、高昂的能耗、频繁的故障以及不菲的成本——极大地阻碍了其普及与实际应用。 晶体管的早期 Point-contact transistor 点接触晶体管 1947年&#xff0c;贝尔实验…

vue2+3 —— Day5/6

自定义指令 自定义指令 需求&#xff1a;当页面加载时&#xff0c;让元素获取焦点&#xff08;一进页面&#xff0c;输入框就获取焦点&#xff09; 常规操作&#xff1a;操作dom “dom元素.focus()” 获取dom元素还要用ref 和 $refs <input ref"inp" type&quo…

报错 No available slot found for the embedding model

报错内容 Server error: 503 - [address0.0.0.0:12781, pid304366] No available slot found for the embedding model. We recommend to launch the embedding model first, and then launch the LLM models. 目前GPU占用情况如下 解决办法: 关闭大模型, 先把 embedding mode…