实验二 系统调用
一、代码理解
在开始编码之前,请阅读xv6 书的第 2 章、第 4 章的第 4.3 和 4.4 节以及相关源文件:
系统调用的用户空间代码位于user/user.h和user/usys.pl中。
内核空间代码是kernel/syscall.h,kernel/syscall.c。
与进程相关的代码是kernel/proc.h和kernel/proc.c。
注意切换分支
$ git fetch
$ git checkout syscall
$ make clean
二、System call tracing
在此作业中,您将添加一个系统调用跟踪功能,该功能可能有助于您调试后续的实验。您将创建一个新的跟踪系统调用来控制跟踪。它应该接受一个参数,即一个整数“掩码”,其位指定要跟踪哪些系统调用。例如,要跟踪 fork 系统调用,程序会调用trace(1 << SYS_fork),其中SYS_fork是kernel/syscall.h中的系统调用编号。如果系统调用的编号在掩码中设置,则必须修改 xv6 内核以在每个系统调用即将返回时打印出一行。该行应包含进程 ID、系统调用的名称和返回值;您不需要打印系统调用参数。跟踪系统调用应该启用对调用它的进程及其随后分叉的任何子进程的跟踪,但不应影响其他进程。
uint64 sys_trace(void)
{
int mask;
if(argint(0, &mask) < 0)
return -1;
myproc()->syscall_trace = mask;
return 0;
}
三、Sysinfo
您将添加一个系统调用sysinfo,用于收集有关正在运行的系统的信息。该系统调用接受一个参数:指向struct sysinfo的指针 (请参阅kernel/sysinfo.h)。内核应填写此结构的各个字段:freemem字段应设置为可用内存的字节数,nproc字段应设置为状态 不是UNUSED 的进程数。我们提供了一个测试程序sysinfotest;如果它打印“sysinfotest: OK”,则您通过了此作业。
uint64
sys_sysinfo(void)
{
// 从用户态读入一个指针,作为存放 sysinfo 结构的缓冲区
uint64 addr;
if(argaddr(0, &addr) < 0)
return -1;
struct sysinfo sinfo;
sinfo.freemem = count_free_mem(); // kalloc.c
sinfo.nproc = count_process(); // proc.c
// 使用 copyout,结合当前进程的页表,获得进程传进来的指针(逻辑地址)对应的物理地址
// 然后将 &sinfo 中的数据复制到该指针所指位置,供用户进程使用。
if(copyout(myproc()->pagetable, addr, (char *)&sinfo, sizeof(sinfo)) < 0)
return -1;
return 0;
}
四、心得体会
系统调用全流程
user/user.h: 用户态程序调用跳板函数 trace()
user/usys.S: 跳板函数 trace() 使用 CPU 提供的 ecall 指令,调用到内核态
kernel/syscall.c 到达内核态统一系统调用处理函数 syscall(),所有系统调用都会跳到这里来处理。
kernel/syscall.c syscall() 根据跳板传进来的系统调用编号,查询 syscalls[] 表,找到对应的内核函数并调用。
kernel/sysproc.c 到达 sys_trace() 函数,执行具体内核操作
记得切换分支➕将trace加入makefilez中(像lab一样)
五、lec3 笔记
复用的同时实现强隔离
进程--对CPU的抽象(缓存亲和性)
exec系统调用--内存的镜像
文件--对磁盘的抽象
防御性--应用程序和操作系统之间必须有强隔离(管理员模式(用户和可以执行特权指令的内核)、虚拟内存(每个进程有它自己的页表))
ecall指令+系统调用编号 将控制权还给内核
宏内核(用户服务和内核服务都保存在相同的地址空间中,它们都由内核进行统一管理)和微内核(massage通信、如何获得高性能)
内核c程序:预编译-〉编译-〉汇编-〉链接;.asm有反编译文件