前言
对于工作了三四年的开发者来说,应该需要有意识的去探索一些计算机底层的知识,这些东西往往都是内功修炼,可以决定我们的技术高度。 今天就来好好学习一下操作系统中内核空间和用户空间到底是怎么回事
一、内核空间和用户空间
以一个32位操作系统为例,他的寻址空间为4G(2^32)。也就是说一个进程跑起来最大的地址空间为4G。
寻址:指的是操作系统能找到的地址范围,32位指的是地址总线的位数,就是32位的二进制数,每一位上只能是0或1,那么32位一共有2的32次方种可能,2的32次方就是可以访问到的最大内存空间,也就是4G。
操作系统的核心是内核(Kernel),他是独立于普通应用程序的,可以访问受保护的内存空间,也有访问底层硬件设备的所有权限。为了保证内核的安全,现在的操作系统一般都强制用户进程不能直接操作内核。具体的实现方式基本上都是由操作系统把内存空间划分为两部分。一部分为内核空间,一部分为用户空间。 对于Linux 系统而言,最高1G的内存空间由内核使用,这部分称为内核空间。剩下的3G内存空间由各个进程使用(应用程序,比如QQ,微信),称为用户空间。
内核空间是操作系统内核访问的区域,独立于普通的应用程序,是受保护的内存空间。
用户空间是普通应用程序可访问的内存区域。
内核空间是对于运行在用户空间中的所有应用程序来说是共享的
二、为什么需要区分内核空间和用户空间
先说结论:是为了提高操作系统的稳定性和可用性。
在CPU的所有指令中,有些指令是非常危险的,如果用错了,很可能会导致系统崩溃,比如清理内存,一旦执行,那么所有正在运行的程序都将被关闭,这个操作就是非常危险的。如果允许所有的程序都可以执行这种指令,那么系统的稳定性和崩溃的概率都没得保障。
所以,CPU将指令分为特权指令和非特权指令,特权指令就是那些比较危险的指令,只允许操作系统以及相关模块使用,普通的应用程序只能使用那些不会造成灾难的非特权指令。
Intel的CPU将特权等级分为四个级别 Ring0 ~ Ring3。而Linux系统实际上只使用了Ring0和Ring3两个级别。当进程运行在Ring3时被称为运行在用户态,运行在Ring0时被称为运行在内核态。
2.1 内核态与用户态
内核态:当进程运行在内核空间时就是内核态。 用户态:当进程运行在用户空间时就是用户态。
在内核态下,CPU可以执行任何指令。运行的代码也不受任何的限制,可以自由地访问任何有效地址。
在用户态下,被执行的代码要受到CPU的诸多检查,他们只能访问映射其地址空间的页表项中规定的在用户态下可访问页面的虚拟地址。其实就是加了一些限制,使进程在用户态下只能去执行那些非特权指令。
对于以前的DOS系统来说,是没有内核空间、用户空间、内核态用户态这些概念的。所有的进程在都可以直接去执行那些CPU的特权指令,也可以理解为DOS系统中所有的进程都运行在内核态。这种方式是很容易让操作系统崩溃的。
对于Linux系统,通过区分内核空间和用户空间的设计,隔离操作系统代码和应用程序代码。即便是单个应用程序出错了,也不会影响操作系统的运行。这样其他运行在用户态的程序还可以正常运行。
三、如何从用户空间进入内核空间
其实所有的系统资源管理器都是在内核空间中完成的。比如读写磁盘文件,分配回收内存,从网络接口读写数据等等。我们的应用程序是无法直接进行这样的操作的。但是我们可以通过内核提供的接口来完成这样的任务。
比如说应用程序要读取磁盘上的一个文件,它可以向内核发起一个系统调用告诉内核要读取磁盘上的哪个文件。这个时候进程才会发生从用户态进入到内核态,其实就是通过一个特殊的指令让进程从用户态进入内核态,在内核空间中,CPU可以执行任何的指令,当然也包括从磁盘上读取数据。具体过程是先把数据读取到内核空间中,然后再把数据拷贝到用户空间并且从内核态转换到用户态。此时应用程序已经从系统调用中返回并且拿到了想要的数据,可以继续往下执行代码。
简单来说就是应用程序把一些操作(比如从磁盘读取文件)外包给了系统内核处理,系统内核专门做这些事情。
对于一个进程来讲,从用户空间进入内核空间并最终返回到用户空间,这个过程是十分复杂且耗费CPU资源的。举个例子,比我我们经常接触的概念堆栈,其实进程在内核态和用户态各维护了一个堆栈。运行在用户空间时进程使用用户空间的堆栈,而运行在内核空间时,进程使用的是内核空间中的堆栈。所以说,Linux中的每个进程有两个栈,分别用于用户态和内核态。
要想使用系统的资源,进程必须从用户态切换成内核态。进程一共有三种方式可以从用户态切换到内核态:系统调用,定时中断,软中断和硬件中断。
四、整体结构
从宏观的角度来看Linux系统的结构,大体可以分为三个部分,从下往上依次为:硬件 -> 内核空间 -> 用户空间。
在硬件之上,内核空间中的代码控制了硬件资源的使用权,用户空间中的代码只能通过内核暴露的系统调用接口才能使用到系统中的硬件资源。
五、总结
现代的操作系统设计基本上都是通过内核空间和用户空间来保护操作系统自身的稳定性和安全性。