当前位置: 首页 > news >正文

[Linux]内核态与用户态详解

内核态和用户态是针对CPU状态的描述。在内核态可以执行一切特权代码,在用户态只能执行那些受限级别的代码。如果需要调用特权代码需要进行内核态切换。

一、内核态和用户态概况

内核态:

  • 系统中既有操作系统的程序,也有普通用户程序。为了安全性和稳定性,操作系统的程序不能随便访问,这就是内核态。即需要执行操作系统的程序就必须转换到内核态才能执行
  • 内核态可以使用计算机所有的硬件资源

用户态:

  • 不能直接使用系统资源,也不能改变 CPU 的工作状态,并且只能访问这个用户程序自己的存储空间!

为什么要区分内核态和用户态?

本质意义是为了进行权限保护。限定用户的程序不能乱搞操作系统,如果人人都能任意读写任意地址空间软件管理就乱套了。例如:

  • 在CPU的所有指令中,有一些指令是非常危险的,如果错用,将导致整个系统崩溃。比如:清内存、设置时钟等。如果所有的程序都能使用这些指令,那么你的系统一天死机N回就不足为奇了。
  • 所以,CPU将指令分为特权指令和非特权指令,对于那些危险的指令,只允许操作系统及其相关模块使用,普通的应用程序只能使用那些不会造成灾难的指令。

二、CPU指令集权限

指令集是CPU实现软件指挥硬件执行的媒介,具体来说每一条汇编语句都对应了一条CPU指令,而非常非常多的CPU指令在一起,可以组成一个、甚至多个集合,指令的集合叫CPU指令集。

同时CPU指令集有权限分级,大家试想,CPU指令集可以直接操作硬件的,要是因为指令操作的不规范,造成的错误会影响整个计算机系统的。好比你写程序,因为对硬件操作不熟悉,导致操作系统内核、及其他所有正在运行的程序,都可能会因为操作失误而受到不可挽回的错误,最后只能重启计算机才行。

而对于硬件的操作是非常复杂的,参数众多,出问题的几率相当大,必须谨慎的进行操作,对开发人员来说是个艰巨的任务,还会增加负担,同时开发人员在这方面也不被信任,所以操作系统内核直接屏蔽开发人员对硬件操作的可能,都不让你碰到这些CPU指令集。
在这里插入图片描述


那么是如何解决上面的问题呢?

针对上面的需求,硬件设备商直接提供硬件级别的支持,做法就是对CPU指令集设置了权限,不同级别权限能使用的CPU指令集 是有限的,以Intel CPU为例,Inter把CPU指令集操作的权限由高到低划为4级:

  • ring 0
  • ring 1
  • ring 2
  • ring 3

Linux系统仅采用ring0和ring3这2个权限。用户态的程序工作在3,内核态的程序处于0

  • ring0权限最高,可以使用所有CPU指令集,有对硬件的所有操作权限
  • ring3权限最低,仅能使用常规CPU指令集,不能使用操作硬件资源的CPU指令集。代码没有对硬件的直接控制权限,也不能直接访问地址的内存,程序是通过调用系统接口(System Call APIs)来达到访问硬件和内存在这里插入图片描述

三、内核态与用户态的空间

1.内核空间和用户空间的划分

在内存资源上的使用,操作系统对用户态与内核态也做了限制,每个进程创建都会分配「虚拟空间地址」,以Linux32位操作系统为例,它的寻址空间范围是4G(2的32次方),而操作系统会把虚拟控制地址划分为两部分,一部分为内核空间,另一部分为用户空间,高位的 1G(从虚拟地址 0xC0000000 到 0xFFFFFFFF)由内核使用,而低位的 3G(从虚拟地址 0x00000000 到 0xBFFFFFFF)由各个进程使用。
在这里插入图片描述

  • 用户态:只能操作0-3G范围的低位虚拟空间地址
  • 内核态:0-4G范围的虚拟空间地址都可以操作,尤其是对3-4G范围的高位虚拟空间地址必须由内核态去操作
  • 补充:3G-4G部分大家是共享的(指所有进程的内核态逻辑地址是共享同一块内存地址),是内核态的地址空间,这里存放着整个内核的代码和所有的内核模块,以及内核所维护的数据。每个进程的4G虚拟空间地址中高位1G都是一样的,即内核空间。只有剩余的3G才归进程自己使用,换句话说就是, 高位1G的内核空间是被所有进程共享的!

最后做个小结,我们通过指令集权限区分用户态和内核态,还限制了内存资源的使用,操作系统为用户态与内核态划分了两块内存空间,给它们对应的指令集使用

2.内核空间、用户空间与物理内存的映射关系

每一个进程都有自己的进程地址空间,该进程地址空间由内核空间和用户空间组成:

  • 用户所写的代码和数据位于用户空间,通过用户级页表与物理内存之间建立映射关系。
  • 内核空间存储的实际上是操作系统代码和数据,通过内核级页表与物理内存之间建立映射关系。

内核级页表是一个全局的页表,它用来维护操作系统的代码与进程之间的关系。因此,在每个进程的进程地址空间中,用户空间是属于当前进程的,每个进程看到的代码和数据是完全不同的,但内核空间所存放的都是操作系统的代码和数据,所有进程看到的都是一样的内容
在这里插入图片描述
需要注意的是,虽然每个进程都能够看到操作系统,但并不意味着每个进程都能够随时对其进行访问。


四、内核态和用户态切换为何提供系统调用?

相信大家都听过这样的话「用户态和内核态切换的开销大」,但是它的开销大在那里呢?简单点来说有下面几点:

  • 保留用户态现场(上下文、寄存器、用户栈等)
  • 复制用户态参数,用户栈切到内核栈,进入内核态
  • 额外的检查(因为内核代码对用户不信任)
  • 执行内核态代码
  • 复制内核态代码执行结果,回到用户态
  • 恢复用户态现场(上下文、寄存器、用户栈等)

实际上操作系统会比上述的更复杂,这里只是个大概,我们可以发现一次切换经历了用户态 —>内核态 —>用户态

用户态要主动切换到内核态要有统一的入口,它们就是内核提供的系统调用接口,下面是Linux整体架构图:
在这里插入图片描述
我们可以看出来通过系统调用将Linux整个体系分为内核态和用户态,而内核提供了一组通用的访问接口,它们使应用程序能访问到内核的资源,如CPU、内存、I/O,这些接口就叫系统调用

  • 库函数就是屏蔽这些复杂的底层实现细节,减轻程序员的负担,从而更加关注上层的逻辑实现,它对系统调用进行封装,提供简单的基本接口给程序员。

  • Shell顾名思义,就是外壳的意思,就好像把内核包裹起来的外壳,它是一种特殊的应用程序,俗称命令行。Shell也是可编程的,它有标准的Shell语法,符合其语法的文本叫Shell脚本,很多人都会用Shell脚本实现一些常用的功能,可以提高工作效率。


五、内核态和用户态切换

如何理解进程切换?

  • 在当前进程的进程地址空间中的内核空间,找到操作系统的代码和数据。
  • 执行操作系统的代码,将当前进程的代码和数据剥离下来,并换上另一个进程的代码和数据。

注意: 当你访问用户空间时你必须处于用户态,当你访问内核空间时你必须处于内核态。

1.用户态切换到内核态的几种方式

  • 系统调用(本质是内中断):用户态进程主动切换到内核态的方式,用户态进程通过系统调用向操作系统申请资源完成工作,例如 fork( )就是一个创建新进程的系统调用,系统调用的机制核心使用了操作系统为用户特别开放的一个中断来实现,如Linux的 int80h 中断,也可以称为软中断
  • 进程的时间片到了,导致进程切换(软中断):属于时钟中断(Timer Interrupt)​的一种实现方式。
  • 异常(内中断):当CPU在执行用户态的进程时,发生了一些没有预知的异常,这时当前运行进程会切换到处理此异常的内核相关进程中,也就是切换到了内核态,如缺页异常
  • 外围设备的中断(硬中断):当CPU在执行用户态的进程时,外围设备完成用户请求的操作后,会向CPU发出相应的中断信号,这时CPU会暂停执行下一条即将要执行的指令,转到与中断信号对应的处理程序去执行,也就是切换到了内核态。如硬盘读写操作完成,系统会切换到硬盘读写的中断处理程序中执行后边的操作等。

2.内核态切换到用户态的几种方式

  • 系统调用返回时。
  • 进程切换完毕。
  • 异常、中断、陷阱等处理完毕。

其中,由用户态切换为内核态我们称之为陷入内核。每当我们需要陷入内核的时,本质上是因为我们需要执行操作系统的代码,比如系统调用函数是由操作系统实现的,我们要进行系统调用就必须先由用户态切换为内核态。


特别鸣谢:
操作系统之内核态与用户态
什么是用户态和内核态?
Linux进程信号

http://www.dtcms.com/a/266814.html

相关文章:

  • 1.1_3_2 三种交换方式的性能分析
  • PHP从字符串到数值的类型转换
  • 后端密码加密:守护用户数据的钢铁长城
  • 第三章 基于rtthread标准库的串口和shell应用
  • vue 循环无限滚动表格
  • 用distance_transform 检测线性凸包
  • Java项目:基于SSM框架实现的忘忧小区物业管理系统【ssm+B/S架构+源码+数据库+毕业论文+开题报告】
  • 双因子认证(2FA)是什么?从零设计一个安全的双因子登录接口
  • Linux-进程概念(3)
  • 在HP暗影精灵Ubuntu20.04上修复IntelAX211Wi-Fi不可用的全过程记录——系统安装以后没有WIFI图标无法使用无线网
  • RabbitMQ 高级特性之 TTL
  • Spring Boot 应用启动时,端口 8080 已被其他进程占用,怎么办
  • 物联网中的Unity/Unreal引擎集成:数字孪生与可视化控制
  • 【Spring Boot】HikariCP 与 Druid 连接池全面对比
  • OpenCV中超分辨率(Super Resolution)模块类cv::dnn_superres::DnnSuperResImpl
  • 数字工厂的核心引擎:物联网驱动生产智能化升级
  • 前端查询条件加密传输方案(SM2加解密)
  • Flink SQLServer CDC 环境配置与验证
  • vue3 el-table 行筛选 设置为单选
  • Oreacle(SQL语言基础)
  • 【问题解决】VSCode终端中看不到Git-Bash
  • XILINX Kintex 7系列FPGA的全局时钟缓冲器(BUFG)和区域时钟缓冲器(BUFR/BUFH)的区别
  • 【PyTorch】PyTorch预训练模型缓存位置迁移,也可拓展应用于其他文件的迁移
  • HTTP协议利用TCP的特性来实现长连接
  • Compose笔记(三十)--图片选择器
  • 【Spring Boot】HikariCP 连接池 YAML 配置详解
  • 洛谷P1941 [NOIP 2014 提高组] 飞扬的小鸟
  • vue3 获取选中的el-table行数据
  • MySQL 查询进阶指南:子查询、多表连接与 UNION 操作全解析
  • SQL 快速参考手册-SQL001