RISC-V 入门 - 系统调用

用户态与内核态

目前为止的学习过程中,所有的程序都是运行在 Machine 模式下,但是在哪决定程序运行在什么模式下的呢?

在学习抢占式多任务时,我们有了创建任务的概念,在汇编代码中有这么一段,使用到了mstatus寄存器:

1
2
3
4
5
6
7
8
9
# Notice: default mstatus is 0
# Set mstatus.MPP to 3, so we still run in Machine mode after MRET.
# Set mstatus.MPIE to 1, so MRET will enable the interrupt.
li t0, 3 << 11 | 1 << 7
csrr a1, mstatus # a1 = mstatus
or t0, t0, a1 # t0 = t0 | a1
csrw mstatus, t0 # mstatus = t0

j start_kernel # hart 0 jump to c

mret返回后,是根据寄存器mstatusMPP来决定接来来是处于什么模式,我们在上面将MPP配置为3MPP的功能是 记录 Machine 模式下,前一个,特权级。这里解实现了在mret之后将模式设置为 Machine 模式(3)。

因为mstatus上电后默认为全 0,所以如果不对其设置,那么在mret之后,就是运行在用户态(0)。

如果想让程序跑在用户态,只要不对齐设置,保持默认即可:

1
2
3
4
5
6
7
8
9
# Notice: default mstatus is 0
# Set mstatus.MPP to 3, so we still run in Machine mode after MRET.
# Set mstatus.MPIE to 1, so MRET will enable the interrupt.
li t0, 1 << 7
csrr a1, mstatus # a1 = mstatus
or t0, t0, a1 # t0 = t0 | a1
csrw mstatus, t0 # mstatus = t0

j start_kernel # hart 0 jump to c

为什么需要系统调用?因为在用户态一些资源(寄存器)的访问是受限的,所以需要封装一些函数,这些函数里会进行模式切换,然后访问需要的资源。

那么如何进行模式的切换呢?这就需要ecall指令。它本质上是触发了异常,就会进入到 Machine 模式处理异常,在 Machine 模式下就相当于在内核态了,就没有访问资源的限制了。

系统模式的切换

ECALL指令实际就是主动触发异常,根据ECALL的权限级别产生不同的异常码,如下图:

从 User 模式调用ECALL异常码等于 8,从 Supervisor 模式调用异常码等于 9,从 Machine 模式调用异常码等于 11。

异常产生时epc寄存器的值存放的是ECALL指令本身的地址。

如果想触发完异常接着往下执行,需要在异常处理逻辑里把 epc 寄存器值改为下一条指令地址,否则会进入死循环。

系统调用的执行流程

系统调用的传参

系统调用作为操作系统的对外接口,由操作系统的实现负责定义。参考 Linux 的系统调用,RVOS 定义系统调用的传参规则如下:

  • 系统调用号放在a7
  • 系统调用参数使用a0-a5
  • 返回值使用a0

系统调用的封装

HEXO 博客嵌入 PDF 浏览器任务栏多窗口命名
You need to set install_url to use ShareThis. Please set it in _config.yml.

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×