實驗的代碼放在了Github上。html
第二個實驗是Lab: system calls。
這個實驗主要就是本身實現幾個簡單的系統調用並添加到XV6中。git
添加系統調用主要有如下幾步:github
在user/user.h
中添加系統調用函數的定義。數組
在user/usys.pl
中添加入口,這個文件將會在make
後生成user/usys.S
文件,在該彙編文件中,每一個函數就只有三行,將系統調用號經過li(load imm)存入a7寄存器,以後使用ecall進入內核態,最後返回。函數
fork: li a7, SYS_fork ecall ret
在kernel/syscall.h
中定義系統調用號。ui
在kernel/syscall.c
的syscalls
函數指針數組中添加對應的函數。在syscall
函數中,先讀取trapframe->a7
獲取系統調用號,以後根據該系統調用號查找syscalls
數組中的對應的處理函數並調用。this
先在proc
結構體中添加一個trace_mask
字段,以後在fork
函數中複製該字段到新進程。指針
在系統調用sys_trace
中就只要經過argint
函數讀取參數,而後設置給trace_mask
字段就好了。code
最後修改syscall
,當系統調用號和trace_mask
匹配時就打印相關信息。htm
// proc.h struct proc { ... // this is for sys_trace() uint trace_mask; }; // proc.c int fork(void) fork(void) { ... // copy trace mask np->trace_mask = p->trace_mask; ... } // sysproc.c uint64 sys_trace(void) { uint mask; if(argint(0, (int*)&mask) < 0) return -1; struct proc *p = myproc(); p->trace_mask |= mask; return 0; } // syscall.c void syscall(void) { int num; struct proc *p = myproc(); num = p->trapframe->a7; if(num > 0 && num < NELEM(syscalls) && syscalls[num]) { uint64 ret = syscalls[num](); p->trapframe->a0 = ret; if((1 << num) & p->trace_mask) { printf("%d: syscall %s -> %d\n", p->pid, syscall_name[num], ret); } } else { printf("%d %s: unknown sys call %d\n", p->pid, p->name, num); p->trapframe->a0 = -1; } }
這一個系統調用主要就是要實現freemem
和nproc
兩個函數來統計內存和進程。
// sysproc.c uint64 sys_sysinfo(void) { uint64 info; // user pointer struct sysinfo kinfo; struct proc *p = myproc(); if(argaddr(0, &info) < 0){ return -1; } kinfo.freemem = freemem(); kinfo.nproc = nproc(); if(copyout(p->pagetable, info, (char*)&kinfo, sizeof(kinfo)) < 0){ return -1; } return 0; }
閱讀kalloc
和kfree
兩個函數就能夠知道,kmem.freelist
是一個保存了當前空閒內存塊的鏈表,所以只須要統計這個鏈表的長度再乘以PGSIZE
就能夠獲得空閒內存。
// kalloc.c uint64 freemem(void) { uint64 counter = 0; struct run *r; acquire(&kmem.lock); r = kmem.freelist; while(r){ r = r->next; ++counter; } release(&kmem.lock); return counter * PGSIZE; }
閱讀procdump
和相關代碼就能夠知道,XV6的進程結構體保存在proc[NPROC]
數組當中。而proc->state
字段保存了PCB的當前狀態,有UNUSED、SLEEPING、RUNNABLE、RUNNING、ZOMBIE五種狀態。所以只須要遍歷這個數組,而後統計state
不是UNUSED狀態的就好了。
// proc.c uint64 nproc(void) { uint64 counter = 0; struct proc *p; for(p = proc; p < &proc[NPROC]; p++) { acquire(&p->lock); if(p->state != UNUSED) { ++counter; } release(&p->lock); } return counter; }