1 用户态与内核态切换
1.1 概念
- 用户态:ring3, 在应用程序中运行,代码没有对硬件的直接控制权限,程序通过调用系统接口来达到访问硬件和内存,在这种模式下,程序发生崩溃也是可以恢复的。
- 内核态:ring0,完全在操作系统的内核中运行,对硬件有所有操作权限,可以执行所有cpu指令集,访问任意地址的内存,在内核模式下异常会导致整台机器停机。
1.2 用户态和内核态的区别
cpu指令集
cpu指令集有权限划分,分为了ring0到ring3,ring3只能使用常规cpu指令集,不能使用操作硬件资源的cpu指令集,比如IO读写,网卡访问,申请内存等。
用户程序想要读写io,必然会用到ring 0级别的cpu指令集,而此时cpu的指令集在ring3级别,需要切换指令集操作权限级别为ring0,cpu再执行相应的ring 0级别的cpu指令集,执行对应的内核代码,会使用到当前进程的内核栈。这里感觉执行操作系统的内核代码其实和用户代码并没有本质区别,权限不同而已。
每个进程都有两个栈,分别是用户栈和内核栈,对应用户态和内核态使用
内存空间
以linux 32位为例,寻址空间是4G,操作系统会把内存空间分配为2部分,一部分为内核空间,另一部分为用户空间,高位的1G由内核使用,低位的3G由各个进程使用,内核态可以操作整个4G的空间,用户态只能操作3G空间。
1.3 用户态和内核态的切换
用户态和内核态切换的主要开销如下:
- 保留用户态现场(上下文、寄存器、用户栈等)
- 用户栈切到内核栈,进入内核态
- 额外的检查(内核代码对用户不信任)
- 执行内核态代码
- 复制内核态代码执行结果,回到用户态
- 恢复用户态现场(上下文、寄存器、用户栈等)
我们可以发现一次切换经历了用户态->内核态->用户态。
用户态切换到内核态的场景:
- 系统调用:用户态进程主动切换到内核态的方式,用户态进程通过系统调用向操作系统申请资源完成工作,比如fork()就是创建新进程的系统调用。
- 异常:当cpu在执行用户态进程时,发生了一些没有预知的异常,这时当前运行进程会切换到处理此异常的内核相关进程中,也就是切换到了内核态。
- 中断: 当cpu执行用户态进程时,外围设备完成用户请求操作后,会向cpu发出中断信号,这时cpu会暂停执行即将执行的指令,转到与中断信号对应的处理程序去执行。