用户态和内核态是如何切换的?
所有的用户进程都是运行在用户态的,但是我们上面也说了,用户程序的访问能力有限,一些比较重要的比如从硬盘读取数据,从键盘获取数据的操作则是内核态才能做的事情,而这些数据却又对用户程序来说非常重要。所以就涉及到两种模式下的转换,即用户态 -> 内核态 -> 用户态,而唯一能够做这些操作的只有 系统调用
,而能够执行系统调用的就只有 操作系统
。
一般用户态 -> 内核态的转换我们都称之为 trap 进内核,也被称之为 陷阱指令(trap instruction)
。
他们的工作流程如下:
- 首先用户程序会调用
glibc
库,glibc 是一个标准库,同时也是一套核心库,库中定义了很多关键 API。 - glibc 库知道针对不同体系结构调用
系统调用
的正确方法,它会根据体系结构应用程序的二进制接口设置用户进程传递的参数,来准备系统调用。 - 然后,glibc 库调用
软件中断指令(SWI)
,这个指令通过更新CPSR
寄存器将模式改为超级用户模式,然后跳转到地址0x08
处。 - 到目前为止,整个过程仍处于用户态下,在执行 SWI 指令后,允许进程执行内核代码,MMU 现在允许内核虚拟内存访问
- 从地址 0x08 开始,进程执行加载并跳转到中断处理程序,这个程序就是 ARM 中的
vector_swi()
。 - 在 vector_swi() 处,从 SWI 指令中提取系统调用号 SCNO,然后使用 SCNO 作为系统调用表
sys_call_table
的索引,调转到系统调用函数。 - 执行系统调用完成后,将还原用户模式寄存器,然后再以用户模式执行。