漫谈操作系统3 -- 基础知识 (用户态和内核态分离)

前两篇文章主要简单介绍了操作系统的分类以及基础的操作系统启动相关的知识,总体感觉有很多基础的知识没有讲清楚,后续的几篇会从最基本的操作系统概念开始介绍,让不论有没有足够经验的朋友都能够迅速打好基础,并且让自己后续的连载不那么突兀。


1. 操作系统的用户态和内核态


操作系统之上运行的软件按照运行的特权级来区分,一般分为用户态进程和内核态程序。 例如在X86体系结构的处理器中,Linux的内核运行在Ring0而相应的所有其他的程序均运行在Ring3, 其运行时状态如下图所示:

漫谈操作系统3 -- 基础知识 (用户态和内核态分离)_第1张图片

而对于ARM处理器,则如下图所示:

漫谈操作系统3 -- 基础知识 (用户态和内核态分离)_第2张图片

ARM处理器的情况稍显复杂,其在EL0运行级运行普通的用户态程序,在EL1运行级运行操作系统内核,在EL2运行级运行虚拟机控制器(VMM),在EL3运行级运行安全状态控制器(Secure Monitor), 而其 EL0/EL1 又分为非安全侧的操作系统,和安全侧的操作系统, 安全侧和非安全侧通过SMC指令陷入EL3进行切换。


2. 用户态和内核态隔离

从上述的介绍我们可以了解到,操作系统的用户态和内核态实际上是基于硬件的运行级实现来实现的,其目的是对软件系统进行隔离,从而达到只有运行在内核态的程序能够访问受限的硬件从而达成用户态程序的权限受限来保证整个系统的安全性和隔离性。


举个例子,  如果没有操作系统的用户态和内核态隔离机制,那么任意的用户态程序都将可以访问任意的硬件设备,例如修改内存访问的页表,访问安全相关的硬件(如指纹,虹膜传感器),那么系统的安全性就无从谈起。而对于有用户态和内核态隔离的操作系统来说,用户态程序如果要访问内核态控制的硬件资源或者系统功能则只能通过系统调用来进行访问,这就大大的减小了暴露给普通应用程序的可访问接口(用安全工作人员的话来说,就是极大的缩小了系统的攻击面)。 

所以用户态和内核态的隔离的主要目的是提升系统的安全性,而从用户态访问内核态则需要通过系统调用进行。


3. 系统调用

系统调用是一种特殊的函数调用,而我们通常所熟悉的比如Linux系统的glibc,以及BSD的libc, 安卓基于BSD libc实现的C基础库,其最重要的功能就是提供对于特定操作系统内核的系统调用POSIX标准接口封装。 所以当我们进行普通的应用程序编写的时候对于任何兼容POSIX的操作系统我们都可以简单的使用  read/write进行文件的读写(文件系统对于一般的类UNIX系统是在内核态实现的), recv/send对网络socket接口进行访问(网络协议栈在类UNIX系统中也是在内核实现的)。 而在其实现上,系统调用的C接口根据硬件处理器架构和底层内核的不同而不同。 

例如对于X86的硬件,要从 ring3 -- > ring0需要使用Int 80 来进行触发,而在内核中其对Int 80的异常处理函数会根据传入的系统调用号(一般是一个整数)的不同而调用相应不同的函数。

而对于ARM的硬件,则需要通过SVC指令进行 EL0 -- > EL1运行级的切换,而内核在对应的svc中断向量处理函数中也会根据不同的系统调用号来调用不同的内核处理函数。


相应的由于内核的不同,例如对于Linux内核,ARM64架构下的sbrk系统调用号为 45 ,  __NR_BRK == 45, 而对于FreeBSD内核系统调用号45 对应的系统调用为ktrace, 所以我们平常所使用的最基本的C库是根据硬件架构的不同和内核的不同而不同的。

 




你可能感兴趣的:(操作系统)