0% 
  
  
  
  Theme NexT works best with JavaScript enabled
 
    
  
  
  
  
    
       
    
       
    
       
      
    
    
    
    
        lab1 syscall 
作业链接 
https://pdos.csail.mit.edu/6.828/2020/labs/syscall.html 
 
xv6-RISCV 的系统调用 
首先用户态程序通过系统调用 ecall
进入内核态,硬件做好一些简单的环境配置,然后进入
trampoline.S/uservec() 程序段,接着通过
syscall()
函数选择具体的系统调用,接着是硬件实现或者软件实现(我们这里做的功能)具体功能。 
 
实验 1: 添加 trace 系统调用 
1. 配置 
在 Makefile 中把 $U/_trace 添加到
UPROGS 里 
 
2. 用户态准备工作 
2.1 user/user.h 
2.2 user/usys.pl 
usys.pl 的作用是生成 usys.S 汇编代码生成用户态的系统调用汇编代码
 
 
1 2 3 4 5 .global trace trace:  li a7, SYS_trace  ecall  ret 
2.3 kernel/syscall.h 
在 kernel/syscall.h 中添加系统调用号 
 
3. 内核代码准备工作 
3.1 kernel/syscall.c 
1 2 3 4 5 6 extern  uint64 sys_trace (void ) ;static  uint64  (*syscalls[]) (void )  = {         [SYS_trace]   sys_trace, }; 
3.2 kernel/sysproc.c 
1 2 3 4 5 6 7 8 uint64 sys_trace (void )  {                    struct  proc  *p  =     printf ("syscall: trace(pid: %d)\n" , p->pid);     return  0 ; } 
1 $ trace 32 grep hello README 
4. 内核代码功能实现 
4.1 实现方式 
在调用 trace 的时候,在进程的 proc
结构中保存一个新的变量
trace_mask,从而在该进程进行系统调用的产生系统调用的输出 
 
4.2 kernel/proc.h 
4.3 kernel/sysproc.c 
1 2 3 4 5 6 7 8 9 uint64 sys_trace (void )  {     struct  proc  *p  =          int  trace_mask;     if (argint(0 , &trace_mask) < 0 )         return  -1 ;     p->trace_mask = trace_mask;     return  0 ; } 
4.4 kernel/syscall.c 
1 2 3 4 5 static  char * syscall_names[] = {    [SYS_fork]    "fork" ,          [SYS_trace]   "trace" , }; 
1 2 3 4 5 6 7 8 9 10 11 12 13 void  syscall (void )  {         if (num > 0  && num < NELEM(syscalls) && syscalls[num]) {         p->trapframe->a0 = syscalls[num]();              if ((1  << num) & p->trace_mask) {         printf ("%d: syscall %s -> %d\n" ,                p->pid, syscall_names[num], p->trapframe->a0);     }     } else  {              } } 
4.5 kernel/proc.c 
1 2 3 4 5 6 7 int  fork (void )  {              np->trace_mask = p->trace_mask;     release(&np->lock);     return  pid; } 
freeproc() 函数中要把 p->trace_mask 置为 0 
 
5. 一些问题 
怎么保证了初始化的结果为全 0
怎么保证 trace_mask 初始化的时候为 0 
  
 
实验 2: 添加 sysinfo 系统调用 
1. 配置 
在 Makefile 中把 $U/_sysinfotest 添加到
UPROGS 里 
 
2. 用户态准备工作 
2.1 user/user.h 
1 2 struct  sysinfo ;int  sysinfo (struct  sysinfo *) ;
2.2 user/usys.pl 
2.3 kernel/syscall.h 
在 kernel/syscall.h 中添加系统调用号 
 
3. 内核代码准备工作 
3.1 kernel/syscall.c 
1 2 3 4 5 6 extern  uint64 sys_sysinfo (void ) ;static  uint64  (*syscalls[]) (void )  = {         [SYS_sysinfo]   sys_sysinfo, }; 
3.2 kernel/sysproc.c 
1 2 3 4 uint64 sys_sysinfo (void )  {          return  0 ; } 
4. 内核代码功能实现 
4.1 kernel/sysproc.c 
主要逻辑就是下列代码注释中所讲的 
通过调用 kernel/kalloc.c 中的函数 get_freemen()
得到空闲的内存大小 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include  "sysinfo.h"  uint64 sys_sysinfo (void )  {          uint64 addr;          if (argaddr(0 , &addr) < 0 )         return  -1 ;          struct  sysinfo  info ;     info.freemem = get_freemen();     info.nproc = get_nproc();          struct  proc * p  =                    if (copyout(p->pagetable, addr, (char *)(&info), sizeof (info)) < 0 )         return  -1 ;     return  0 ; } 
4.2 kernel/defs.h 
1 2 uint64          get_freemen (void ) ; uint64          get_nproc (void ) ; 
4.2 kernel/kalloc.c 
实现函数 get_freemen(),获取当前空闲内存的字节数 
 
1 2 3 4 5 6 7 8 9 10 11 uint64 get_freemen ()  {     uint64 num = 0 ;     struct  run  *r ;     r = kmem.freelist;          while (r){         ++num;         r = r->next;     }     return  num * PGSIZE; } 
struct kmem 记录当前进程的空闲内存信息 
struct run 是一个链表,记录空闲的内存 
 
1 2 3 4 5 6 7 8 struct  run  {  struct  run  *next ; }; struct  {  struct  spinlock  lock ;   struct  run  *freelist ; } kmem; 
4.3 kernel/proc.c 
实现函数 get_nproc(),获取当前状态为 UNUSED
的进程数 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 uint64 get_nproc (void ) {     uint num = 0 ;     uint i = 0 ;     for (; i < NPROC; ++i) {                  acquire(&(proc[i].lock));         if (proc[i].state != UNUSED) {             ++num;         }         release(&(proc[i].lock));     }     return  num; } 
实验结果 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 $ make qemu-gdb trace 32 grep: OK (11.5s) == Test trace all grep == $ make qemu-gdb trace all grep: OK (1.7s) == Test trace nothing == $ make qemu-gdb trace nothing: OK (1.8s) == Test trace children == $ make qemu-gdb trace children: OK (21.7s) == Test sysinfotest == $ make qemu-gdb sysinfotest: OK (5.1s) == Test time == time: OK Score: 35/35 
遇到的困难以及收获 
做完这个 lab 之后对系统调用的认识更加深入了 
困难主要是在于第一次写
lab,刚开始读源代码还是有点吃力,但是读完了收获还是很大的 
 
对课程或 lab 的意见和建议 
mit 的 lab 想到写的很清晰,循循善诱,让我们一步一步深入 xv6
的内核设计 
希望中文版也能跟上,毕竟我们也是自己开课的 
 
参考文献 
https://pdos.csail.mit.edu/6.828/2019/xv6/book-riscv-rev0.pdf