用户态与内核态
用户态、内核态其实是CPU运行权限的概念,我们知道处理器指令分为两类,特权指令和非特权指令,用户是不能直接使用特权指令的,不然用户可以随意调度敏感资源,整个系统就混乱了。
x86 CPU的权限有四个层级,分别是Ring0~Ring3.其中Ring0的级别最高,可以使用所有指令(内核运行在Ring0上),而用户代码运行在Ring3上,只能使用非特权指令。
如何确定CPU能否执行特权指令呢,这里就要说到CPU的运行状态了。如果CPU此时的状态是用户态,那么它是无法执行特权指令的,而当CPU的状态转换为内核态后,就可以执行所有指令了。因此,用户态和内核态其实是指CPU的运行状态
用户空间与内核空间
下图是一个典型进程地址空间:
从图中可以发现,进程地址空间可以分为两部分,内核空间和用户空间。内核空间对用户代码是不可见的。
系统调用过程
用户空间的代码是运行在CPU用户态下的,所以是无法直接使用特权指令的,所幸操作系统给用户提供了某些接口,借助这些接口,用户可以让操作系统帮忙完成某些自己无法完成的操作。这些接口就是系统调用了,下面举一个例子。
假设用户代码调用write()这个系统调用,此时控制流转到了内核空间即CPU开始执行内核空间的代码,同时CPU运行状态也进入了内核态。write()这个系统调用的行为是把要写的数据存放到内核的IO缓存区就马上返回,这个IO缓存区也在内核空间。当write系统调用返回以后,控制流就回到用户空间,而CPU也回到了用户态。这整个过程结束,其实并没有涉及进程切换。
这里我们可以发现,每一个进程都有这样一块内核空间,他们都是一样的。但显然操作系统不会蠢到把这块代码复制这么多份,他们其实映射向了同一块物理内存(虚拟内存和物理内存的知识不再赘述)。所以是一个类似下图的结构:
内核代码在内存中实际只存在一份,但是被映射向了各个进程的内核空间,这和共享库其实是有异曲同工之妙的。
结语
这里只是很粗略得谈了系统调用的控制流过程,对内核空间等概念有一个较清晰的解释。但其中各方面的细节其实还很多,不过这些知识已经可以帮助我们对一个系统调用,如IO操作的执行流程有了一个正确的认识,对后续问题的分析和解决打下了一个基础。