🗒️XV6-System Calls
2023-5-30|2023-5-30
Anthony
type
status
date
slug
summary
tags
category
icon
password
本Blog基于任职于美国波特兰洲立大学的教授@hhp3在Youtube上对xv6内核的讲解视频本期讲解xv6中的系统调用,视频链接在下面
前置知识
任何一个用户态的程序想要使用系统调用都需要包含头
users.h
一段汇编代码
usys.s
包含了一系列汇编函数,在用户态可以运行syscall.h
定义了包括内核态和用户态使用系统调用的函数代码
user.h
任何一个想要使用系统调用的程序都需要包含user.h。该头文件定义了所有系统调用的原型,
对于user.h中的任何一个调用,xv6都会执行一段汇编程序(usys.S)。usys.S是由usys.pl自动生成的。

usys.s or usys.pl
usys.s对于每一个系统调用都创建了如下的类似代码
如果我们用系统调用sleep()来举例子,那么生成的汇编代码应该是
- 其中全局变量的定义将使得其能被链接
- 其中的syscall.h定义了系统调用的编号,这些编号将会被用户态的程序使用,用来指定需要的系统调用。对于sleep,编号是13。
Usertrap
该函数定义在trap.c中
其中最重要的部分是
if(r_scause() == 8)
的判断。在xv6中,如果将要执行一个系统调用,r_scause()获取到的值会是8。在判断完成这是一个系统调用之后,系统首先判断给该进程是否要被杀死
p->trapframe->epc += 4;
目的是为了系统调用返回之后执行下一条指令。此后将中断设置成允许,执行系统调用,在返回之后再次检查是否需要杀死进程,以及判断该中断是否是一个时间片中断。最后调用usertrapret()
syscall
该函数在syscall.c中
在syacall中,其获取了之前就存在a7寄存器中的系统调用编号
进行检查之后,然后在syscalls数组中找到指向系统调用函数的指针,然后激活这个系统调用。
Helper Functions for syscall
一个系统调用的简单RoadMap
- 当前为用户态,调用了trap()
- trap()
- 将系统从用户态变成内核态
- 将中断设置为不允许
- 保存程序计数器
- 调用uservec函数
- uservec是一段汇编代码,它保存在trampoline page 中,它将
- 保存用户寄存器
- 加载内核寄存器
- 将虚拟地址空间转换成内核虚拟地址空间
- 调用usertrap()
- usertrap()会通过一定的方法判断出这是一个系统调用,执行完系统调用之后调用usertrapret()
- 执行系统调用
- usertrapret()将会调用userret
- userret是一段汇编代码,他将系统恢复成用户态离开时候的模样,调用sret
- sret
- 将系统状态变成用户态
- 恢复程序计数器
- 将中断设置成允许
需要注意的是,当usertrap()判断完这是一个系统调用之后,中断将被允许,也就是说系统调用也是可以被打断的,但是最终我们还是会执行完系统调用
以zombie.c为例,展示用户态函数sleep是如何转换成系统调用sleep的
我们点击zombie.c的sleep,查找其引用,发现在user.h中有定义。
如上文所述,在user.h中的任何一个函数,xv6都会为其生成一段汇编代码,并且暴露全局变量允许链接。以sleep为例子,则生成的汇编代码是
接下来ecall执行系统调用,在syscall函数中接收到了num = 13于是执行sys_sleep
在syssleep中执行了内核中的sleep函数。这样子我们就走完了以此用户态执行系统调用的流程