實驗的代碼放在了Github上。
第二個實驗是Lab: system calls。
這個實驗主要就是自己實現幾個簡單的系統調用並添加到XV6中。
XV6系統調用
添加系統調用主要有以下幾步:
在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
中定義系統調用號。
在kernel/syscall.c
的syscalls
函數指針數組中添加對應的函數。在syscall
函數中,先讀取trapframe->a7
獲取系統調用號,之后根據該系統調用號查找syscalls
數組中的對應的處理函數並調用。
System call tracing (moderate)
先在proc
結構體中添加一個trace_mask
字段,之后在fork
函數中復制該字段到新進程。
在系統調用sys_trace
中就只要通過argint
函數讀取參數,然后設置給trace_mask
字段就行了。
最后修改syscall
,當系統調用號和trace_mask
匹配時就打印相關信息。
// 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;
}
}
Sysinfo (moderate)
這一個系統調用主要就是要實現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;
}