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

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

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


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


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值,继续使用自己的用户态堆栈。


相关文章
|
JSON 前端开发 JavaScript
JSON 必知必会
JSON 必知必会
1371 0
|
机器学习/深度学习 计算机视觉
深度学习常见的损失函数
深度学习常见的损失函数
1118 1
深度学习常见的损失函数
|
机器学习/深度学习 算法 数据可视化
python/matlab图像去雾/去雨综述
python/matlab图像去雾/去雨综述
1558 120
|
10月前
|
存储 运维 监控
毅硕HPC | Lustre文件系统在HPC集群中的部署实战
Lustre作为一种开源的、分布式并行文件系统,凭借其卓越的可扩展性和极高的I/O吞吐量,成为了全球Top 500超算中心的首选。Lustre不仅仅是一个存储系统,它是连接计算资源与科学数据的纽带。
894 0
|
存储 JavaScript 开发者
探索鸿蒙新世界:ArkUI框架实战指南,解锁HarmonyOS应用UI设计的无限可能!
【10月更文挑战第19天】ArkUI框架是华为鸿蒙系统中用于开发用户界面的核心工具,支持ArkTS和eTS两种开发语言。本文介绍了ArkUI的基本概念、组件使用、布局管理和状态管理,通过示例代码帮助开发者轻松构建美观、高效的跨设备UI。
1503 3
|
缓存 人工智能 算法
Nvidia_Mellanox_CX5和6DX系列网卡_RDMA_RoCE_无损和有损_DCQCN拥塞控制等技术简介-一文入门RDMA和RoCE有损无损
Nvidia_Mellanox_CX5和6DX系列网卡_RDMA_RoCE_无损和有损_DCQCN拥塞控制等技术简介-一文入门RDMA和RoCE有损无损
5232 0
|
算法 数据可视化 数据挖掘
C# | DBSCAN聚类算法实现 —— 对直角坐标系中临近点的点进行聚类
聚类算法是一种常见的数据分析技术,用于将相似的数据对象归类到同一组或簇中。其中,DBSCAN(Density-Based Spatial Clustering of Applications with Noise)是一种基于密度的聚类算法,能够有效地识别出不同形状和大小的簇,同时还能标识出噪声数据。本篇博客将介绍聚类算法的概念、DBSCAN算法的原理,并通过提供的C#代码逐步解析DBSCAN算法的实现过程。
1293 0
C# | DBSCAN聚类算法实现 —— 对直角坐标系中临近点的点进行聚类
|
数据处理 C语言
深入解析x86架构:X86, X86_32和X86_64的差异与应用
深入解析x86架构:X86, X86_32和X86_64的差异与应用
|
开发框架 运维 安全
无影云电脑,畅享极速渲染新体验
本文介绍了渲染行业面临的挑战及无影云电脑带来的革新。传统渲染依赖高性能本地设备,面临重投入、性能固定、灵活性差和数据安全问题。无影云电脑通过云端算力,实现了灵活扩展、任意终端接入及按需付费,大幅提升了渲染效率与体验,尤其在游戏设计、工业制造等领域表现突出。其核心技术确保了色彩还原、流畅操作及外设兼容性,满足专业设计师的需求。未来,无影将助力渲染行业实现更高效、安全的云端工作模式。
|
存储 NoSQL MongoDB
Redis在中国火爆,为何MongoDB更受欢迎国外?
本文介绍了Redis和MongoDB的基本概念及其在GitHub Star、DB-Engines Ranking和Google Trends中的数据对比。Redis是一个基于内存的键值对存储数据库,适合快速读写场景;MongoDB则是面向文档的数据库,支持大规模数据存储和复杂查询。全球范围内,MongoDB的搜索热度高于Redis,但在中国市场,Redis更受欢迎,因其高性能和低延迟特性满足了中国互联网公司对高并发的需求。总结部分分析了两者的特点及适用场景,并结合中美两国的行业背景解释了其受欢迎程度的不同原因。
682 1

热门文章

最新文章