实验的代码放在了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; }