用户态与内核态
目前为止的学习过程中,所有的程序都是运行在 Machine 模式下,但是在哪决定程序运行在什么模式下的呢?
在学习抢占式多任务时,我们有了创建任务的概念,在汇编代码中有这么一段,使用到了mstatus
寄存器:
1 |
|
mret
返回后,是根据寄存器mstatus
的MPP
来决定接来来是处于什么模式,我们在上面将MPP
配置为3
, MPP
的功能是 记录 Machine 模式下,前一个,特权级。这里解实现了在mret
之后将模式设置为 Machine 模式(3)。
因为mstatus
上电后默认为全 0,所以如果不对其设置,那么在mret
之后,就是运行在用户态(0)。
如果想让程序跑在用户态,只要不对齐设置,保持默认即可:
1 |
|
为什么需要系统调用?因为在用户态一些资源(寄存器)的访问是受限的,所以需要封装一些函数,这些函数里会进行模式切换,然后访问需要的资源。
那么如何进行模式的切换呢?这就需要ecall
指令。它本质上是触发了异常,就会进入到 Machine 模式处理异常,在 Machine 模式下就相当于在内核态了,就没有访问资源的限制了。
系统模式的切换
ECALL
指令实际就是主动触发异常,根据ECALL
的权限级别产生不同的异常码,如下图:
从 User 模式调用ECALL
异常码等于 8,从 Supervisor 模式调用异常码等于 9,从 Machine 模式调用异常码等于 11。
异常产生时epc
寄存器的值存放的是ECALL
指令本身的地址。
如果想触发完异常接着往下执行,需要在异常处理逻辑里把 epc 寄存器值改为下一条指令地址,否则会进入死循环。
系统调用的执行流程
系统调用的传参
系统调用作为操作系统的对外接口,由操作系统的实现负责定义。参考 Linux 的系统调用,RVOS 定义系统调用的传参规则如下:
- 系统调用号放在
a7
中 - 系统调用参数使用
a0-a5
- 返回值使用
a0
评论