一文彻底理解用户态和内核态


之前被面试官问到过一个问题:用户态和内核态有什么区别,最近正好在写操作系统专题的文章,拿出来和大家聊聊这个话题,当然理解好这个问题,肯定会有助于你理解操作系统。

正在阅读的你,有没有想过,操作系统里面讲到的进程、线程,如果分用户态和内核态的话,到底是怎么样存在于操作系统的,又是怎么样调度的呢,用户态线程和内核态线程有什么区别呢?一系列问题,铺天盖地来了,那么今天一起来彻底搞定它。

首先来看看用户态和内核态是什么?

用户态、内核态是什么

用户态(user mode)在计算机结构指两项类似的概念。在CPU的设计中,用户态指非特权状态。在此状态下,执行的代码被硬件限定,不能进行某些操作,比如写入其他 进程 的存储空间,以防止给 操作系统 带来安全隐患。在 操作系统 的设计中,用户态也类似,指非特权的执行状态。 内核 禁止此状态下的代码进行潜在危险的操作,比如写入系统配置文件、杀掉其他用户的 进程 、重启系统等。
引用了一段科学百科的定义,通俗来讲,用户态就是作为用户的我们能够操作的资源的一种形态。那么内核态就是系统内核所能操作的资源的一种形态。

这里提到了两种资源,内核态资源和用户态资源,有两种资源,那么肯定有两种访问权限,需要对着两种资源进行权限控制,其实就是我们CPU工作模式里将到的特权级,CPU特权级分为四级,R0~R3,那么下面来看看CPU指令集的权限控制。

CPU指令集权限控制

CPU指令其实也是分权限的。低权限的指令和高权限的指令所能访问的资源不一样,这是因为CPU是直接操作硬件的,如果因为操作不当不规范,那么会影响整个计算机系统,关于硬件的操作非常复杂,操作不当出现问题的概率也非常大必须得谨慎使用,然后如果把这个任务留个计算机使用或者开发人员,这无疑是灾难性的。

所以操作系统充当了这个中间者过程,它屏蔽了底层硬件的复制,抽象出一个个系统调用接口供给开发人员使用。由于本身CPU指令集也分权限,主要分为Ring0~Ring3 这四个级别,高级别权限可访问低级别权限资源,其中我们之前提到的用户态拥有最低权限Ring3,而内核具有最高权限Ring0,可以访问整个计算机资源。

这么就是CPU指令分权限,下面来看看内核态和用户态的区别。

用户态和内核态区别

上面讲了那么多文字,不符合我的风格,直接来图。

这么一看图,非常情绪,内核态所对应的内核空间就是一个,而用户态所对应的用户空间就随着进程而有多个了。果然是一图胜千言。

解读一下上图:上图是以32位Liunx操作系统为例的。可以看到内核态在位置是在34G,而用户的位置是在03G的,也就是说用户态只能操作0~3G的低位虚拟空间地址,而用户态0-4G的空间地址都可以操作,而且3-4G必须要在内核态中操作,而且可以看到3-4G的位置是各个进程共享的。

也就是说每个进程拥有自己独立的4G虚拟空间,而最高位1G是一样的,剩下的3G才是自己进程使用。

用户态与内核态切换

上面提到了有些资源只有内核态能够访问,所以这些都必须由内核态去执行,那么从用户态切换到内核态就需要有消耗,那么这些开销在哪里呢?主要如下:

  • 保存现场(寄存器上下文等)

  • 复制参数&权限检查,将这些参数从用户函数栈切到内核函数栈,进入内核态

  • 执行内核代码

  • 复制结果到用户态,切回用户态

  • 恢复现场
    实际的切换操作比这个复杂很多,下面来看看常见会发生模态切换的情形:

  • 系统调用,系统调用时内核抽象出的给用户态调用的接口,这里无疑会从进行模态切换。

  • 异常,当CPU在执行用户态程序时,发生了一些无法预知的异常,这个时候保证程序的正常运行,内核态会接管过来,比如内存管理todo那一篇文章提到的Page fault。

  • 中断,中断是值CPU暂停当前执行的指令,切换到与之对应信号的程序去处理程序,比如I/O中断等等。

用户、内核线程模型

用户态对应使用用户线程,内核态对应使用内核线程。

当用户线程需要发送一个网络请求的时候,我们知道用户线程是没有权限对网卡等IO设备Ring0级别进行访问的,这个时候怎么办呢?那就得求助与我们的内核线程了,也就是下面要讲到的用户、内核线程模型问题。

在进入主题之前,我们先来看一下:task_struct 这个数据结构,你可以简单理解为所有的线程、进程都有与之对应的一个这样的数据结构,在进程中,它叫做PCB –> PROCESS control block,进程控制块,而对于线程来讲,它叫做TCB –> thread control block 也就是线程控制块。

如图,用户进程的PCB存在于内核空间中,TCB也位于内核空间中,这样内核就可以对TCB进行调度,因为线程是执行任务的最小单位嘛,所以讲到的CPU调度问题,就是针对TCB。

由于用户线程无法访问Ring0的资源,所以就得有与之对应的内核线程进行接管,也就是上面提到的用户态和内核态的切换,内核线程接管之后会进行一系列处理,然后把结果复制给用户线程,那么这样一次Ring0资源的访问就完成了。

下面来看看三种对应的模型。

1:1模型

如上图,1对1模型是最简单的一种模型,简单来说就是一个用户线程对应一个内核线程,这里是指用户线程对应内核线程,反过来不一定。这样用户线程就具有了内核线程的权限,可以访问更高级别的资源了,这类模型可以让多线程程序在多处理器的系统上有非常好的表现,但它由有两点缺点:

  • 很多操作系统限制了内存线程的数量,因此不是每个用户线程都能很好的对应上内核线程,这就受到了限制。
  • 操作系统内核线程调度的时候,上下文切换开销大, 这也就会导致用户线程执行效率低。

    n:1模型

如上图,线程的切换开始由用户线程来进程,这样减小了内核线程切换的代价,所以这种模型线程切换更快速,但是也有最大的问题:如果一个用户线程阻塞了,那么与之对应的内核线程不与其他用户线程对应,也就是所有线程都无法工作,这个时候就出现阻塞情况。

n:n模型

如上图,这类模型拥有了前面两种模型的优化,一个用户线程阻塞不影响整体,在多核CPU系统上,它提升的性能不能1对1模型高。

总结

用户态中运行着程序执行的基本单位——进程,用户进程在内核中有专门的PCB来管理,而一个进程有多个线程,每个线程在内核中有TCB来管理,他们是两个表进行管理的。用户线程需要访问内核资源权限的时候就切换到与之对应的内核线程执行。

更多内容你可以关注公众号: 「ConeZhang


文章作者: Cone
版权声明: 本博客所有文章除特別声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 Cone !
  目录