系统调用与用户态陷入内核态

简介: 我们都知道操作系统中运行着很多的进程,如果普通的进程可以直接操作硬件那么这个系统的安全性没办法保障,所以操作系统分出了两种状态,一种状态是运行的代码可以操作硬件;一种状态不能操作硬件,只能切换到第一种状态去操作后再切换回来,这就是内核态和用户态。

我们都知道操作系统中运行着很多的进程,如果普通的进程可以直接操作硬件那么这个系统的安全性没办法保障,所以操作系统分出了两种状态,一种状态是运行的代码可以操作硬件;一种状态不能操作硬件,只能切换到第一种状态去操作后再切换回来,这就是内核态和用户态。


在用户态下,进程只能访问自己的虚拟地址空间和一些受限的资源,不能直接访问系统硬件设备或其他进程的地址空间;而在内核态下,进程可以访问系统的全部资源,包括硬件设备和其他进程的地址空间。内核态比用户态拥有更高的权限和更多的系统资源。当进程需要访问系统资源时,比如进行文件读写、创建新进程等操作,就需要通过系统调用的方式从用户态切换到内核态来执行相应的操作。在进程执行系统调用之前,它的运行状态处于用户态;而在系统调用执行期间,进程的运行状态会从用户态切换到内核态;系统调用执行完毕后,进程又会从内核态切换回用户态。这种用户态和内核态的切换是操作系统实现进程间资源隔离和保护的重要手段。


1、特权级别位

为了保护操作系统和应用程序的安全性,将CPU指令划分为不同的权限级别,并对不同的指令权限级别进行不同的访问限制。一般来说,CPU指令权限划分可以分为四个级别,分别是内核态、用户态、超级用户态和虚拟机态,CPU会为每个指令设置相应的权限级别。当一个程序运行时CPU会检查程序所使用的指令是否有足够的权限级别来执行,如果权限级别不够就会抛出异常,这个异常会被操作系统捕获,并转交给内核进行处理,内核会决定代表程序执行受限的操作或终止程序。


CPU通过特权级别位来区分当前程序是运行在用户态还是内核态,只有运行在内核态时才有足够的权限执行内核态指令


特权级别位存储在CPU的控制寄存器中,具体来说,是存储在处理器状态字(Processor Status Word,PSW)或类似的寄存器中。在x86架构的CPU中,PSW包含了多个标志位,其中包括特权级别位。特权级别位通常称为处理器状态字的"特权位"(Privilege Bit),它可以是一个单独的标志位,也可以是一个多位的标志位。

在x86架构的CPU中,特权级别位通常被设置为两种状态:用户态和内核态。特权级别位被称为特权级别(Privilege Level,PL),它有两种状态:PL0表示最高的特权级别,即内核态;PL3表示最低的特权级别,即用户态。当CPU执行一个指令时,它会根据这个指令的权限级别来检查当前特权级别位的值,如果指令的权限级别高于当前特权级别,则会产生异常,这个异常可以被操作系统内核捕获并处理。

需要注意的是,不同CPU架构的特权级别位可能存储在不同的寄存器中,而且具体的实现方式也有所不同。因此,在编写操作系统内核或者底层驱动程序时,需要特别注意特权级别位的存储和使用方式,以确保程序的正确性和可移植性。

2、用户态陷入内核态

当用户态的程序想要执行特权操作时,就需要切换到内核态去执行。而陷入内核态也是一种特权操作,用户程序不能直接陷入内核态,只能通过系统调用或者在发生中断或异常时才会陷入用户态,其中系统调用是唯一一种由用户程序主动发起的陷入内核态的方式


  1. 系统调用(System Call):用户程序可以通过系统调用向操作系统内核发起请求,请求操作系统内核执行一些特权操作,如创建进程、打开文件、读写设备等。系统调用的执行过程中,用户程序需要陷入内核态,以便操作系统内核能够响应用户程序的请求。
  2. 异常(Exception):当处理器执行指令时发生异常(如缺页异常、非法操作码等),处理器会自动切换到内核态,并将控制权交给操作系统内核来处理异常。操作系统内核可以根据异常的类型和信息来进行相应的处理,如分配物理页面、关闭程序等。
  3. 中断(Interrupt):当外部设备发生中断事件时,处理器会自动切换到内核态,并将控制权交给操作系统内核来处理中断。操作系统内核可以根据中断的类型和信息来进行相应的处理,如读取输入设备的数据、发送输出数据等。

3、系统调用

系统调用(System Call)是操作系统提供给用户程序的接口,通过系统调用,用户程序可以请求操作系统内核来执行一些特权操作,如创建进程、打开文件、读写设备等。系统调用的执行过程可以概括为以下几个步骤:


  1. 用户程序调用系统函数,将系统调用的参数传递给系统函数。
  2. 系统函数将系统调用的参数存储到寄存器中,并将系统调用号(syscall number)存储到寄存器eax中。
  3. 系统函数执行中断指令int 0x80,触发CPU从用户态切换到内核态,将控制权交给操作系统内核。
  4. 操作系统内核根据系统调用号和参数,执行相应的特权操作,如创建进程、打开文件、读写设备等。
  5. 操作系统内核将特权操作的结果返回给用户程序,将控制权重新切换到用户态,继续执行用户程序。



IDT表(Interrupt Descriptor Table)是指中断描述符表,它是操作系统内核用来管理和响应中断、异常和系统调用等事件的数据结构之一。

在x86架构的计算机中,IDT表是一个由256个条目(或者说表项)组成的数组,每个表项都包含一个处理程序的地址和相关的标志信息,用于响应特定类型的中断或异常。其中,前32个表项用于响应CPU预定义的中断和异常,称为异常描述符表(Exception Descriptor Table);第33到第255个表项用于用户自定义的中断、异常和系统调用等事件,称为中断描述符表(Interrupt Descriptor Table)。

**当发生一个中断或异常时,CPU会查找IDT表中对应的表项,根据其中存储的处理程序地址跳转到相应的处理程序中执行。**如果处理程序执行成功,则可以对中断或异常进行处理并返回;否则,处理程序可能会抛出异常、生成错误信息或者直接导致系统崩溃等。

int 0x80的IDT表中的DPL被设置成了3,所以才能从用户态能直接调用int 0x80的中断指令(因为用户态的CPL为3,小于等于DPL),从而陷入内核态。

也就是如果强制在用户态代码中埋一些特权指令那么执行的时候会因为权限不足而报错,想要执行特权指令就只能进入内核态才能执行,而要主动进入内核态只能通过系统调用去调用内核定义的系统函数,也就是只能通过系统提供的合法、安全的方式来执行,不能自己直接调用特权指令,从而提高了系统的安全性。


4、内核空间与用户空间

需要注意的是,操作系统内核和用户程序在物理上并没有区别,它们在同一块内存中,只是在执行时CPU通过特权级别位来区分当前程序执行指令的权限级别,限制了一定要通过系统函数执行特权指令。但是系统函数常驻在内核空间,我们还需要保护这部分函数不被修改。所以操作系统内核需要通过一些特殊的手段来保证自己的安全性和稳定性,例如使用不同的内存地址空间来隔离不同的程序,限制程序的访问范围等。


内核是指内存管理、文件管理、硬件管理等这些模块的集合,可以看作是一个全局共享的,有权限保护功能的最底层的库,本质上是一个二进制文件,常驻在内存中。而系统调用是进程和内核交互的本质,当有进程想做一些高特权的操作时,如文件读写,就会通过系统调用去执行它的部分代码。开机之后,引导程序就会将内核载入内存。


在现代操作系统中,内存空间通常被划分为内核空间和用户空间。内核空间是操作系统内核所在的内存区域,它包含了操作系统内核的所有代码和数据结构,以及受保护的硬件资源,如中断控制器、时钟、设备驱动程序等。用户空间是普通应用程序所在的内存区域,它包含了应用程序的代码和数据结构,但是不能直接访问受保护的内核空间资源。


在x86架构的CPU中,内核空间通常被限制在地址空间的最高端,而用户空间则被限制在地址空间的最低端。具体来说,在32位的x86架构中,内核空间通常被限制在0xC0000000及以上的地址范围,而用户空间则被限制在0x00000000至0xBFFFFFFF之间的地址范围。这样做的目的是为了避免用户程序越界访问内核空间的代码和数据,从而提高操作系统的安全性和稳定性。


每次访问内存时,需要通过比较DPL(目标内存段的特权级,存在段描述符中)和CPL(当前特权级,也叫进程特权级,存在cs寄存器的低两位)来判断有是否足够的权限级别访问。只有DPL>=CPL时,才允许访问只有DPL>=CPL时,才允许访问。而内核段的DPL在内核初始化阶段就都设置为了0,所以内核态可以访问任何数据,而用户态不能访问内核数据。


5、内核栈与用户栈切换

在x86架构的计算机中,每个进程都有自己的内核栈和用户栈,它们分别用于处理内核态和用户态的函数调用。在进程被创建时,操作系统会为其分配一定数量的虚拟内存空间,其中包括用户栈和内核栈。


当进程发起系统调用或中断请求进入内核态时,处理器会动将当前用户态的栈指针(ESP)和标志寄存器(EFLAGS)压入进程内核态堆栈中,同时把内核态堆栈指针加载到ESP中此时进程就开始在内核态运行。在内核态执行完系统调用或中断服务例程后,处理器会自动从内核态的堆栈中取出之前保存的用户态堆栈指针,并将其加载到ESP寄存器中,以便返回到用户态继续执行。这样,用户态进程就可以通过恢复之前的ESP值,继续使用自己的用户态堆栈。


相关文章
|
API Windows
我的免杀之路:虚拟保护
虚拟保护技术利用的是 Windows API 中的 VirtualProtect 函数,是对应 Win32 函数的逻辑包装函数,它会在呼叫处理程序的虚拟位置空间里,变更认可页面区域上的保护。
925 0
我的免杀之路:虚拟保护
|
8月前
|
人工智能 算法 数据格式
DeepSeek 开源周第二弹!DeepEP:专为 MoE 训练和推理设计的并行通信库
DeepEP 是 DeepSeek 开源的首个专为混合专家模型(MoE)训练和推理设计的通信库,支持高吞吐量、低延迟通信,优化 NVLink 和 RDMA 网络性能。
557 3
|
11月前
|
调度
系统库调用如何在内核模式和用户模式之间切换?
【10月更文挑战第29天】系统库调用通过特定的指令触发从用户模式到内核模式的切换,传递参数并保存上下文,在内核模式下完成系统服务的处理后,再恢复上下文并返回用户模式。这种模式切换机制是操作系统实现用户程序与内核交互的核心机制,保证了系统的稳定性、安全性和资源的有效管理。同时,异常和中断处理也会涉及到类似的模式切换,共同构成了操作系统复杂而高效的运行机制。
201 4
|
存储 人工智能 自然语言处理
DeepSparse: 通过剪枝和稀疏预训练,在不损失精度的情况下减少70%的模型大小,提升三倍速度
该论文提出了一种新方法,用于创建高稀疏性大型语言模型,通过稀疏预训练和高效部署,在保持高准确度的同时显著提升处理速度。方法包括结合SparseGPT剪枝和稀疏预训练,实现70%稀疏度下准确率完全恢复,尤其适合复杂任务。实验显示,使用Cerebras CS-3 AI加速器和Neural Magic的DeepSparse、nm-vllm引擎,训练和推理速度有显著提升。此外,量化稀疏模型在CPU上速度提升可达8.6倍。这种方法优于传统剪枝,为构建更快、更小的语言模型提供了新途径,并通过开源代码和模型促进了研究复现和扩展。
425 3
|
11月前
|
网络协议 安全 算法
网络空间安全之一个WH的超前沿全栈技术深入学习之路(9):WireShark 简介和抓包原理及实战过程一条龙全线分析——就怕你学成黑客啦!
实战:WireShark 抓包及快速定位数据包技巧、使用 WireShark 对常用协议抓包并分析原理 、WireShark 抓包解决服务器被黑上不了网等具体操作详解步骤;精典图示举例说明、注意点及常见报错问题所对应的解决方法IKUN和I原们你这要是学不会我直接退出江湖;好吧!!!
网络空间安全之一个WH的超前沿全栈技术深入学习之路(9):WireShark 简介和抓包原理及实战过程一条龙全线分析——就怕你学成黑客啦!
|
11月前
|
JSON JavaScript 前端开发
Go语言中json序列化的一个小坑,建议多留意一下
在Go语言开发中,JSON因其简洁和广泛的兼容性而常用于数据交换,但其在处理数字类型时存在精度问题。本文探讨了JSON序列化的一些局限性,并介绍了两种替代方案:Go特有的gob二进制协议,以及msgpack,两者都能有效解决类型保持和性能优化的问题。
274 7
|
12月前
|
前端开发
前端:行内元素的 margin 和 padding
在前端开发中,行内元素的处理是至关重要的。行内元素默认与其他元素在同一行显示,不会占据独立的空间。对于行内元素而言,其外边距(margin)和内边距(padding)的设置有特定规则:垂直方向上的外边距和内边距不会影响其他元素,但水平方向的则会增加元素的宽度,可能会影响相邻元素的位置。合理设置这些属性,可以有效控制页面布局与间距。
|
API 算法框架/工具
【threejs教程】三维物体与三维向量
【8月更文挑战第7天】threejs教程:三维物体与三维向量
265 3
【threejs教程】三维物体与三维向量
|
Web App开发 资源调度 前端开发
electron 中如何安装或更新 vuejs-devtool 最新稳定版
electron 中如何安装或更新 vuejs-devtool 最新稳定版
|
Java 测试技术 程序员
基于SpringBoot+Vue交流和分享平台的设计与实现(源码+部署说明+演示视频+源码介绍)(2)
基于SpringBoot+Vue交流和分享平台的设计与实现(源码+部署说明+演示视频+源码介绍)
254 1