目录

  1. 概述
  2. 操作系统中的双空间
    1. 内核空间
    2. 用户空间
    3. 空间切换
    4. 操作系统双空间总结
  3. 操作系统中的双模式
    1. 用户模式(用户态)
    2. 内核模式(内核态)
      1. 内核抢占特性
      2. 内核可重入特性
    3. 模式切换
  4. 双模式与双空间总结
  5. 附录

虽然现在普遍使用的是 32 位系统,但是系统空间划分在很早以前就提出了。位数更多的系统代表寻址范围更大,能够使用的内存更多,和本文讨论的问题没有冲突。所以本文简单的介绍在32 位系统下的内核空间(Kernel Space)与用户空间(User Space)的相关概念

概述

对 32 位操作系统而言,它的寻址空间(虚拟地址空间,或叫线性地址空间)为 4G($2^{32}$),然而这并不代表全部的地址空间都可以提供给用户使用。操作系统为了自身安全考虑,将这 4G 大小的空间,人为的分成两个部分:内核空间与用户空间。

内核(kernel)是操作系统的核心,它独立于普通的应用程序,可以访问受保护的内存空间,也有访问底层硬件设备的所有权限。为了保证内核的安全,现在的操作系统一般都强制用户进程不能直接操作内核(需要模式切换)。

🎶 整个内核,它不是一个进程,而是一个进程的控制器

✨ 针对 linux 操作系统而言

  • 将最高的 1G 字节(从虚拟地址 0xC0000000 到 0xFFFFFFFF),供内核使用,称为内核空间。这个空间是所有进程共享的
  • 将较低的 3G 字节(从虚拟地址 0x00000000 到 0xBFFFFFFF),供各个进程使用,称为用户空间

更具体的图片如下图所示:

操作系统中的双空间

❓为什么需要区分内核空间与用户空间?

在 CPU 的所有指令中,有些指令是非常危险的,如果错用,将导致系统崩溃,比如清内存、设置时钟等。如果允许所有的程序都可以使用这些指令,那么系统崩溃的概率将大大增加。所以,CPU 将指令分为特权指令非特权指令,对于那些危险的指令,只允许操作系统及其相关模块使用,普通应用程序只能使用那些不会造成灾难的指令

👍 将操作系统分为两种空间有如下优点:

  1. 操作系统的数据都是存放于系统空间的,用户进程的数据是存放于用户空间的;
  2. 分开来存放,就让系统的数据和用户的数据互不干扰,保证系统的稳定性,并且管理上很方便;
  3. 将用户的数据和系统的数据隔离开,就可以对两部分的数据的访问进行控制。如此就可以确保用户程序不能随便操作系统的数据,防止用户程序误操作或者是恶意破坏系统

总而言之,在类 Unix 操作系统中将系统内存划分为用户空间和内核空间对于维护系统稳定性和安全性具有重要作用

比如 Intel 的 CPU 将特权等级分为 4 个级别:Ring0~Ring3。其实 Linux 系统只使用了 Ring0 和 Ring3 两个运行级别(Windows 系统也是一样的),当进程运行在 Ring3 级别时被称为运行在用户态,而运行在 Ring0 级别时被称为运行在内核态

这是一种用来在发生故障时保护数据和功能,提升容错度,避免恶意操作 ,提升计算机安全的设计方式,Rings 是从最高特权级(通常被叫作 0 级)到最低特权级(通常对应最大的数字)排列的。在大多数操作系统中,Ring 0 拥有最高特权,并且可以和最多的硬件直接交互(比如 CPU,内存)

内核空间

内核是构成计算机操作系统中央核心的程序。它不是一个进程,而是一个进程的控制器,它可以完全控制系统上发生的一切。这包括管理用户空间内的单个用户进程并防止它们相互干扰等。

用户进程只能通过使用 系统调用 来访问内核空间。系统调用是类 Unix 操作系统中活动进程对内核执行的服务的请求,例如输入/输出(I/O) 或进程创建。活动进程是当前在 CPU 中进行的进程,与在 CPU 中等待其下一次运行的进程相反。I/O 是将数据传入或传出 CPU 以及传入或传出外围设备(如磁盘驱动器、键盘、鼠标和打印机)的任何程序、操作或设备

用户空间

用户空间是用户进程在其中运行的系统内存的部分,这与内核空间形成对比,内核空间是内核在其中执行并提供其服务的那部分内存

空间切换

所有的系统资源管理都是在内核空间中完成的,比如读写磁盘文件,分配回收内存,从网络接口读写数据等等。应用程序是无法直接进行这样的操作的,但是可以通过内核提供的接口来完成这样的任务。比如应用程序要读取磁盘上的一个文件,它可以向内核发起一个 「 系统调用 」 告诉内核:「 我要读取磁盘上的某某文件 」。

其实就是通过一个特殊的指令让进程从用户态(运行在用户空间)进入到内核态(运行在内核空间中)。在内核空间中,CPU 可以执行任何的指令,当然也包括从磁盘上读取数据。具体过程是先把数据读取到内核空间中,然后再把数据拷贝到用户空间并从内核态切换到用户态。

此时应用程序已经从系统调用中返回并且拿到了想要的数据,可以继续往下执行。简单说就是应用程序把高科技的事情(从磁盘读取文件)外包给了系统内核,让系统内核做这些事情既专业又高效「 让专业的人干专业的事 」。

对于一个进程来讲,从用户空间进入内核空间并最终返回到用户空间的过程,是十分复杂的。举个例子,比如我们经常接触的概念 「 堆栈 」,其实进程在内核态和用户态各有一个堆栈

运行在用户空间时进程使用的是用户空间中的堆栈,而运行在内核空间时,进程使用的是内核空间中的堆栈。所以说,Linux 中每个进程有两个栈,分别用于用户态和内核态。

下图简明的描述了用户态与内核态之间的转换:

概括的说,有三种方式:系统调用、软中断和硬件中断能够让用户态的进程切换到内核态使用系统的资源

Linux 系统的中断服务程序不在进程的上下文中执行,它们在一个与所有进程都无关的、专门的中断上下文中执行。之所以存在一个专门的执行环境,就是为了保证中断服务程序能够在第一时间响应和处理中断请求,然后快速地退出

操作系统双空间总结

从内核空间和用户空间的角度看整个 Linux 系统的结构,它大体可以分为三个部分,从下往上依次为:硬件、内核空间、用户空间。如下图所示:

在硬件之上,内核空间中的代码控制了硬件资源的使用权,用户空间中的代码只有通过内核暴露的系统调用接口(System Call Interface)才能使用到系统中的硬件资源。其实,不光是 Linux,Windows 操作系统的设计也是大同小异。

基于以上对系统空间的划分,我们可以将每个处理器在任何指定时间点上的活动概括为下列三者之一:

  • 运行于用户空间,执行用户进程
  • 运行于内核空间,处于进程上下文,代表某个特定的进程执行
  • 运行于内核空间,处于中断上下文,与任何进程无关,处理某个特定的中断

以上三点几乎包括所有的情况,比如当 CPU 空闲时,内核就运行一个空进程。

现代的操作系统大都通过内核空间和用户空间的设计来保护操作系统自身的安全性和稳定性

操作系统中的双模式

一个程序中的错误可能会对许多进程产生不利影响,它可能会修改另一个程序的数据,也可能会影响操作系统。内核模式下的进程有权访问任何设备和内存,同时内核模式下的任何崩溃都会导致整个系统崩溃。而在用户模式下的任何崩溃只会导致错误的进程停止。因此,操作系统中的双模式能够有效的隔离错误扩散范围,减少系统崩溃原因。

例如,如果一个进程陷入无限循环,那么这个无限循环可能会影响其他进程的正确操作。所以为了保证操作系统的正确执行,有两种操作模式:用户模式内核模式

用户模式(用户态)

用户模式是所有进程运行时的正常模式,但是此模式仅具有有限的访问权限。当计算机系统由用户应用程序运行时,如:创建文本文档或使用任何应用程序,则系统此时处于用户模式。当用户应用程序向操作系统请求服务或发生中断活系统调用时,将从用户模式切换到内核模式以完成请求

下图描述了中断发生时的情况:

内核模式(内核态)

内核模式也称为特权模式,其中进程可以不受限制地访问系统资源,如:硬件、内存等、进程可以访问 I/O 硬件寄存器对其进行编程,可以在内核模式下执行操作系统内核代码并访问内核数据等。

系统启动时,硬件以内核模式启动;操作系统加载时,它以用户模式启动用户应用程序。为了对硬件提供保护,操作系统仅在内核模式下执行相关特权指令。如果用户试图在用户模式下运行特权指令,那么它会将指令视为非法并拒绝执行。与进程管理、IO 硬件管理和内存管理相关的任何事情都需要进程在内核模式下执行。

✨ 操作系统中一些常见的特权指令是:

  • 处理中断
  • 从用户模式切换到内核模式
  • 输入输出管理

内核抢占特性

Linux 内核在 2.4 版中是不可抢占的,也就是说,当一个进程处于内核模式时,除非它自愿放弃对 CPU 的控制,否则它不能在其获取的时间片内被另一个进程挂起和替换(即被抢占)。但是需要注意的是:内核模式中的进程可以被操作系统中的中断或异常所打断

Linux 内核版本 2.6 是抢占式的。也就是说:可以挂起在内核模式下运行的进程,这对于实时应用程序来说是一个非常重要的特性

内核可重入特性

类 Unix 内核是可重入的,这意味着多个进程可以同时处于内核模式。但是,在单处理器系统上,无论哪种模式(用户模式或内核模式),在任何时间点都只有一个进程在 CPU 中运行,而其他进程将被暂时阻塞,直到轮到它们为止。

模式切换

所有进程都是从用户态开始执行,只有在获得内核提供的服务时才切换到内核态。这种模式间的更改称为模式切换不要与上下文切换混淆,后者是 CPU 从一个进程切换到另一个进程

当用户进程通过系统调用运行部分内核代码时,该进程暂时成为内核进程并处于内核模式。在内核模式下,进程将拥有 root 特权以及对关键系统资源的访问权限。当内核满足进程的请求时,它会将进程返回到用户模式

对于任何系统,特权模式和非特权模式对于访问保护都很重要。处理器必须具有用户/内核模式的硬件支持。系统调用接口 (System Call Interface) 是从用户空间到内核空间的唯一途径。内核空间切换是通过软件中断来实现的,它改变处理器模式,将 CPU 执行跳转到中断处理程序,执行相应的系统调用例程

双模式与双空间总结

当进程运行在内核空间时就处于内核态,而进程运行在用户空间时则处于用户态

在内核态下,进程运行在内核地址空间中,此时 CPU 可以执行任何指令。运行的代码也不受任何的限制,可以自由地访问任何有效地址,也可以直接进行端口的访问

在用户态下,进程运行在用户地址空间中,被执行的代码要受到 CPU 的诸多检查,它们只能访问规定的虚拟地址范围,且只能对任务状态段(TSS)中 I/O 许可位图(I/O Permission Bitmap)中规定的可访问端口进行直接访问

对于以前的 DOS 操作系统来说,是没有内核空间、用户空间以及内核态、用户态这些概念的。可以认为所有的代码都是运行在内核态的,因而用户编写的应用程序代码很容易就让操作系统崩溃掉。

对于 Linux 来说,通过区分内核空间和用户空间的设计,隔离了操作系统代码(操作系统的代码要比应用程序的代码健壮很多)与应用程序代码,因此就变向的减少了系统崩溃几率。即便是单个应用程序出现错误也不会影响到操作系统的稳定性,这样其它的程序还可以正常的运行。所以,区分内核空间和用户空间本质上是要提高操作系统的稳定性及可用性

附录

操作系统为什么需要内核空间和用户空间?
Kernel Space Definition
User Space Definition
分级保护域
Kernel Mode Definition
User Mode Definition
Dual Mode operations in OS