🗒️XV6-System Calls

2023-5-30|2023-5-30
Anthony
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自动生成的。
notion image
 

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

  1. 当前为用户态,调用了trap()
  1. trap()
    1. 将系统从用户态变成内核态
    2. 将中断设置为不允许
    3. 保存程序计数器
    4. 调用uservec函数
  1. uservec是一段汇编代码,它保存在trampoline page 中,它将
    1. 保存用户寄存器
    2. 加载内核寄存器
    3. 将虚拟地址空间转换成内核虚拟地址空间
    4. 调用usertrap()
  1. usertrap()会通过一定的方法判断出这是一个系统调用,执行完系统调用之后调用usertrapret()
  1. 执行系统调用
  1. usertrapret()将会调用userret
  1. userret是一段汇编代码,他将系统恢复成用户态离开时候的模样,调用sret
  1. sret
    1. 将系统状态变成用户态
    2. 恢复程序计数器
    3. 将中断设置成允许
需要注意的是,当usertrap()判断完这是一个系统调用之后,中断将被允许,也就是说系统调用也是可以被打断的,但是最终我们还是会执行完系统调用
 
 

以zombie.c为例,展示用户态函数sleep是如何转换成系统调用sleep的

我们点击zombie.c的sleep,查找其引用,发现在user.h中有定义。
 
如上文所述,在user.h中的任何一个函数,xv6都会为其生成一段汇编代码,并且暴露全局变量允许链接。以sleep为例子,则生成的汇编代码是
接下来ecall执行系统调用,在syscall函数中接收到了num = 13于是执行sys_sleep
在syssleep中执行了内核中的sleep函数。这样子我们就走完了以此用户态执行系统调用的流程
XV6-FileSystem-StructureMini Game:Connect four