lab2: syscall
trace
本实验主要是实现一个追踪系统调用的函数,那么首先根据提示定义trace系统调用,并修复编译错误。
首先看一下user/trace.c的内容,主要的代码如下
if (trace(atoi(argv[1])) < 0) {fprintf(2, "%s: trace failed\n", argv[0]);exit(1);}for(i = 2; i < argc && i < MAXARG; i++){nargv[i-2] = argv[i];}exec(nargv[0], nargv);
它首先调用trace(int),然后将命令行中的参数argv复制到nargv中,同时删去前两个参数,例如
argv = trace 32 grep hello READMEnargv = grep hello README
那么,根据提示,我们首先再proc结构体中添加一个数据字段,用于保存trace的参数。并在sys_trace()的实现中实现参数的保存
// kernel/proc.hstruct proc {// ...int trace_mask; // trace系统调用参数};// kernel/sysproc.cuint64sys_trace(void){// 获取系统调用的参数argint(0, &(myproc()->trace_mask));return 0;}
接下来应当考虑如何进行系统调用追踪了,根据提示,这将在syscall()函数中实现。下面是实现代码,需要注意的是条件判断中使用了&而不是==,这是因为在实验说明书的例子中,trace 2147483647 grep hello README将所有31个低位置为1,使得其可以追踪所有的系统调用。
voidsyscall(void){int num;struct proc *p = myproc();num = p->trapframe->a7; // 系统调用编号,参见书中4.3节if(num > 0 && num < NELEM(syscalls) && syscalls[num]) {p->trapframe->a0 = syscalls[num](); // 执行系统调用,然后将返回值存入a0// 系统调用是否匹配if ((1 << num) & p->trace_mask)printf("%d: syscall %s -> %d\n", p->pid, syscalls_name[num], p->trapframe->a0);} else {printf("%d %s: unknown sys call %d\n",p->pid, p->name, num);p->trapframe->a0 = -1;}}
在上面的代码中,我们还有一些引用的变量尚未定义,在syscall.c中定义他们
// ...extern uint64 sys_trace(void);static uint64 (*syscalls[])(void) = {// ...[SYS_trace] sys_trace,};static char *syscalls_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",};
sysinfo
- 在kernel/kalloc.c中添加一个函数用于获取空闲内存量
struct run {struct run *next;};struct {struct spinlock lock;struct run *freelist;} kmem;
内存是使用链表进行管理的,因此遍历kmem中的空闲链表就能够获取所有的空闲内存,如下
voidfreebytes(uint64 *dst){*dst = 0;struct run *p = kmem.freelist; // 用于遍历acquire(&kmem.lock);while (p) {*dst += PGSIZE;p = p->next;}release(&kmem.lock);}
- 在kernel/proc.c中添加一个函数获取进程数
遍历proc数组,统计处于活动状态的进程即可,循环的写法参考scheduler函数
voidprocnum(uint64 *dst){*dst = 0;struct proc *p;for (p = proc; p < &proc[NPROC]; p++) {if (p->state != UNUSED)(*dst)++;}}
- 实现
sys_sysinfo,将数据写入结构体并传递到用户空间
uint64sys_sysinfo(void){struct sysinfo info;freebytes(&info.freemem);procnum(&info.nproc);// 获取虚拟地址uint64 dstaddr;argaddr(0, &dstaddr);// 从内核空间拷贝数据到用户空间if (copyout(myproc()->pagetable, dstaddr, (char *)&info, sizeof info) < 0)return -1;return 0;}
