1.2 CPU指令集原理
CPU可提供指令集,操作系统通过指令集实现其自身功能。主流指令集包括x86指令集、ARM指令集和MIPS指令集等。本节将以x86指令集为例介绍CPU指令集原理。
1.2.1 特权指令集和非特权指令集
x86体系下所有CPU可以执行的指令集被人为地分为两类:特权指令集和非特权指令集。特权指令是指有特殊权限的指令。这类指令的权限比较大,一般是一些操作和管理关键系统资源的指令,如果使用不当,则会导致严重后果乃至整个系统的崩溃,所以设置了权限,不让所有程序都能执行特权指令。如果在非最高特权级上运行,特权指令会引发异常,处理器会直接跳转(Trap)到最高特权级,交由系统软件处理。除特权指令之外的指令称为非特权指令,非特权指令不改变系统的状态。常见的特权指令有:
允许和禁止中断。
在进程间切换处理。
存、取用于主存保护的寄存器。
执行I/O操作。
停止一个中央处理器的工作。
清理内存。
设置时钟。
建立存储键。
加载程序状态字(PSW)。
1.2.2 保护模式及内核态、用户态
为了保护数据和阻止恶意访问,x86 体系在引入特权指令集之后又额外引入了保护模式。相同指令在不同保护模式下,被CPU处理的方式不同,指令在执行时所拥有的权限和所能处理的资源也都不同。
在x86架构下,CPU内的寄存器有2个比特位用来指定当前CPU所在的保护模式。所以保护模式提供4种特权级别,呈环形保护结构,最内层的环具有最高权限Ring 0,从内到外特权级别依次降低,最外层Ring 3特权级别最低,如图1-3所示。当指令访问内存地址时,会读取CPU中的某个寄存器来确认当前指令所在的安全等级,同时操作系统会将内存分为多个安全等级(Ring 0~Ring 3)。
图 1-3
内存地址中的 CPL 表示该内存地址的安全等级。当指令需要读/写内存时,会对比指令执行时的安全等级(内核态还是用户态)及内存地址的安全等级,来确定该指令是否有权限对该内存进行读/写。
Linux只用到了Ring 0和Ring 3。其中特权指令只有在最高特权级别Ring 0下才能执行,而非特权指令可以在Ring 0或Ring 3下执行,但相同的非特权指令在不同保护模式下执行的行为不同。当指令在CPU最高特权级别Ring 0下执行时,其具有访问系统中一切资源的权限,这时指令的状态称为“内核态”。当指令在CPU最低特权级别Ring 3下执行时,其只具有访问部分资源的权限,此种状态称为“用户态”。当程序不在Ring 0下运行时,是无法监听 Ring 1~Ring 3下的其他程序的。
1.2.3 指令工作流程
当应用运行在用户态时,如果有操作系统资源等危险行为,则会通过操作系统的“陷阱(Trap)机制”转到内核态,然后执行相应的系统资源操作。在完成操作之后,又会从内核态回到用户态。从用户态到内核态的“陷阱机制”通常有如下三种触发条件。
系统调用:当在用户态需要操作系统级别的服务时,会主动通过系统调用的指令进入内核态。例如,malloc函数其实就是通过系统调用让内核为其分配线性地址空间的。
异常:比如缺页异常(有的书上叫“缺页中断”,这么说其实不够严谨);再如用户执行特权指令产生异常。
硬件中断:当有硬件中断时,操作系统需要去响应外部中断,如果此前正在用户态执行,则会进入内核态的中断处理函数中。比如硬盘读/写操作完成后,系统会切换到硬盘读/写的中断处理程序中,然后执行后续操作。
总体来说,当应用运行时,如果涉及使用或改变系统资源的操作,则会触发“陷阱机制”。首先陷阱会被CPU捕获,然后CPU会调用操作系统内核中相应的例程(routine程序)执行对应的操作。当操作系统内核执行完对应的操作之后,它会返回用户态下的应用,继续执行应用剩下的操作。以上过程如图1-4所示。
图 1-4