lab2中,我们将给xv6增加一些新的系统调用
System call tracing (moderate) YOUR JOB  添加一个系统调用跟踪功能,该功能可能会在以后调试实验时有所帮助。创建一个新的trace系统调用来控制跟踪。它应该有一个参数,这个参数是一个整数“掩码”(mask),它的比特位指定要跟踪的系统调用。例如,要跟踪fork系统调用,程序调用trace(1 << SYS_fork),其中SYS_fork是kernel/syscall.h中的系统调用编号。如果在掩码中设置了系统调用的编号,则必须修改xv6内核,以便在每个系统调用即将返回时打印出一行。该行应该包含进程id、系统调用的名称和返回值;不需要打印系统调用参数。trace系统调用应启用对调用它的进程及其随后派生的任何子进程的跟踪,但不应影响其他进程。hints 
在Makefile的UPROGS中添加$U/_trace 
运行make qemu,将看到编译器无法编译user/trace.c,因为系统调用的用户空间存根还不存在:将系统调用的原型添加到user/user.h,存根添加到user/usys.pl,以及将系统调用编号添加到kernel/syscall.h,Makefile调用perl脚本user/usys.pl,它生成实际的系统调用存根user/usys.S,这个文件中的汇编代码使用RISC-V的ecall指令转换到内核。一旦修复了编译问题(注:如果编译还未通过,尝试先make clean,再执行make qemu),就运行trace 32 grep hello README;但由于还没有在内核中实现系统调用,执行将失败。 
在kernel/sysproc.c中添加一个sys_trace()函数,它通过将参数保存到proc结构体(请参见kernel/proc.h)里的一个新变量中来实现新的系统调用。从用户空间检索系统调用参数的函数在kernel/syscall.c中,您可以在kernel/sysproc.c中看到它们的使用示例。 
修改fork()(请参阅kernel/proc.c)将跟踪掩码从父进程复制到子进程。 
修改kernel/syscall.c中的syscall()函数以打印跟踪输出。需要添加一个系统调用名称数组以建立索引。 
 
在Makefile中添加用户程序trace.c 1 2 3 4 5 6 UPROGS=\ 	$U/_cat\ 	$U/_echo\     ··· 	$U/_zombie\ 	$U/_trace\ 
 
在用户空间(user文件夹)中引入trace系统调用原型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 // user/user.h // system calls int fork(void); int exit(int) __attribute__((noreturn)); ··· int uptime(void); int trace(int); // user/usys.pl entry("fork"); entry("exit"); ··· entry("uptime"); entry("trace"); 
 
在内核空间中添加系统调用编号 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 // kernel/syscall.h // System call numbers #define SYS_fork    1 #define SYS_exit    2 ··· #define SYS_close  21 #define SYS_trace  22 // kernel/syscall.c extern uint64 sys_trace(void); static uint64 (*syscalls[])(void) = { [SYS_fork]    sys_fork, [SYS_exit]    sys_exit, ··· [SYS_close]   sys_close, [SYS_trace]   sys_trace, }; 
 
添加sys_trace()函数 这里需要将解析的参数保存到process之中,所以需要在proc结构体中添加新变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 // kernel/sysproc.c uint64 sys_trace(void) {   int n;      if (argint(0, &n) < 0) {     return 1;   }   myproc()->trace_code = n;   return 0; } // kernel/proc.h // Per-process state struct proc {   ···   // these are private to the process, so p->lock need not be held.   uint64 kstack;               // Virtual address of kernel stack   uint64 sz;                   // Size of process memory (bytes)   ···   char name[16];               // Process name (debugging)   int trace_code; }; 
 
这里用到了kernel/syscall.c的argint函数用来解析参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 static uint64 argraw(int n) {   struct proc *p = myproc();   switch (n) {     case 0:       return p->trapframe->a0;     case 1:       return p->trapframe->a1;     case 2:       return p->trapframe->a2;     case 3:       return p->trapframe->a3;     case 4:       return p->trapframe->a4;     case 5:       return p->trapframe->a5;   }   panic("argraw");   return -1; } // Fetch the nth 32-bit system call argument. int argint(int n, int *ip) {   *ip = argraw(n);   return 0; } 
 
int argint(int n, int *ip) : 用于获取第 n 个 系统调用参数 调用 argraw() 函数获取参数值,然后通过指针 ip 把值写入到调用者提供的内存位置。
static uint64 argraw(int n) : 接受一个整型参数 n 作为输入,并返回一个 64 位无符号整型 (uint64) 的结果 函数内部首先使用 myproc() 函数获取当前进程的结构体 (struct proc *p) 然后根据输入参数 n 的值,返回进程陷阱帧 (p->trapframe) 中相应的参数值(p->trapframe->a0, p->trapframe->a1, …)
通过argint()解析参数,赋值给proc中的trace_code
修改fork() 由于struct proc中增加了一个新的变量,当fork的时候我们也需要将这个变量传递到子进程中
1 2 3 4 5 6 // kernel/proc.c int fork(void) {     ···     np->trace_code = p->trace_code;     ··· } 
 
修改syscall()打印输出内容 这里要添加一个系统调用名称数组以建立索引
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 // kernel/syscall.c static char *syscall_name[] = { [SYS_fork]    "fork", [SYS_exit]    "exit", [SYS_wait]    "wait", [SYS_pipe]    "pipe", [SYS_read]    "read", [SYS_kill]    "kill", [SYS_exec]    "exec", [SYS_fstat]   "fstat", [SYS_chdir]   "chdir", [SYS_dup]     "dup", [SYS_getpid]  "getpid", [SYS_sbrk]    "sbrk", [SYS_sleep]   "sleep", [SYS_uptime]  "uptime", [SYS_open]    "open", [SYS_write]   "write", [SYS_mknod]   "mknod", [SYS_unlink]  "unlink", [SYS_link]    "link", [SYS_mkdir]   "mkdir", [SYS_close]   "close", [SYS_trace]   "trace", }; void syscall(void) {   int num;   struct proc *p = myproc();   num = p->trapframe->a7;   if (num > 0 && num < NELEM(syscalls) && syscalls[num]) {     p->trapframe->a0 = syscalls[num]();     if ((1 << num) & p->trace_code) {       printf("%d: syscall %s -> %d\n", p->pid, syscall_name[num], p->trapframe->a0);     }   } else {     printf("%d %s: unknown sys call %d\n", p->pid, p->name, num);     p->trapframe->a0 = -1;   } } 
 
Sysinfo (moderate) YOUR JOB  在这个作业中,您将添加一个系统调用sysinfo,它收集有关正在运行的系统的信息。系统调用采用一个参数:一个指向struct sysinfo的指针(参见kernel/sysinfo.h)。内核应该填写这个结构的字段:freemem字段应该设置为空闲内存的字节数,nproc字段应该设置为state字段不为UNUSED的进程数。我们提供了一个测试程序sysinfotest;如果输出“sysinfotest: OK”则通过。hints 
在Makefile的UPROGS中添加$U/_sysinfotest 
当运行make qemu时,user/sysinfotest.c将会编译失败,遵循和上一个作业一样的步骤添加sysinfo系统调用。要在user/user.h中声明sysinfo()的原型,需要预先声明struct sysinfo的存在:1 2 3 struct sysinfo; int sysinfo(struct sysinfo *); 
 
sysinfo需要将一个struct sysinfo复制回用户空间;请参阅sys_fstat()(kernel/sysfile.c)和filestat()(kernel/file.c)以获取如何使用copyout()执行此操作的示例。 
要获取空闲内存量,请在kernel/kalloc.c中添加一个函数 
要获取进程数,请在kernel/proc.c中添加一个函数 
 
准备工作 参考前一个task ,在user和kernel中添加sysinfo系统调用
1 2 3 4 5 // user/sysinfo.h struct sysinfo {   uint64 freemem;   // amount of free memory (bytes)   uint64 nproc;     // number of process }; 
 
两个任务:进程数/空闲内存 进程数 在kernel/proc.c中对所有proc进行遍历,如果proc->state不为UNUSED状态,则计数加1
1 2 3 4 5 6 7 8 9 10 // kernel/proc.c void free_proc_num(uint64 *ans) {   struct proc *p;   *ans = 0;   for (p = proc; p < &proc[NPROC]; p++) {     if (p->state != UNUSED) {       (*ans)++;     }   } } 
 
空闲内存 在访问keme.freelist时,为了保护现场安全需给其上锁,返回memory byte,每次+=PGSIZE
1 2 3 4 5 6 7 8 9 10 11 12 // kernel/kalloc.c void free_mem(uint64 *ans) {   *ans = 0;   struct run *p = kmem.freelist;   acquire(&kmem.lock);   while (p) {     *ans += PGSIZE;     p = p->next;   }   release(&kmem.lock); } 
 
添加新定义的函数 在kernel/defs.h中加入刚刚写的两个函数
sys_sysinfo() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 // kernel/sysproc.c struct sysinfo {   uint64 freemem;   // amount of free memory (bytes)   uint64 nproc;     // number of process }; uint64 sys_sysinfo(void) {   struct sysinfo info;   free_mem(&info.freemem);   free_proc_num(&info.nproc);   uint64 dstaddr;   argaddr(0, &dstaddr);   // 从内核空间拷贝数据到用户空间   if (copyout(myproc()->pagetable, dstaddr, (char *)&info, sizeof info) < 0) {     return -1;   }   return 0; }