系列文章目录
MIT 6.s081 实验解析——labs1
MIT 6.s081 实验解析——labs2
文章目录
- 系列文章目录
- 测试判断流程
- System call tracing
- sysinfo![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/ab9ca34f1fc64b6aa1df74613dc1a397.png)
测试判断流程
- 完成代码后将.c文件放入user文件夹中
- 在makefile文件的UPROGS处添加要测试的文件,如要添加的是sleep.c,则写为_sleep。
- 重新编译xv6
make qemu
- 退出qemu,在文件夹下输入
./grade-lab-util <文件名>//以sleep为例
./grade-lab-util sleep
System call tracing
trace 32 grep hello README
以上述指令来说,这个实验想要实现的效果是,跟踪grep hello README过程中所有的系统调用,其中32为掩码,是要跟踪的系统调用种类,将32变为2进制,根据下图,在要跟踪的系统调用位置置1。
所以先更改kernel/proc.h的进程结构体,新增掩码:
struct proc {...int mask;};
为了实现进程间传递参数,需添加对mask的拷贝,在kernel/proc.c的fork定义中:
intfork(void){...// Cause fork to return 0 in the child.np->trapframe->a0 = 0;np->mask = p->mask;...}
接下来就是要去完成系统调用 trace 的函数定义,在该系统调用会接收用户态传递的参数,并将其赋值给mask,我们将定义写在kernel/sysproc.c中:
uint64sys_trace(void){int n;argint(0,&n);//接收参数myproc()->mask = n;//赋给maskreturn 0;}
为了在syscall中能调用sys_trace,我们需要将其函数入口地址存入数组static uint64 (*syscalls[])(void)中,在其末尾加入[SYS_trace] sys_trace,即可。
最后我们要在syscall调用完系统调用后,通过本进程的mask来确认这个系统调用是不是被追踪的,若是则输出相关信息,为了方便信息输出,我们为其定义一个字符串数组,修改的文件为kernel/syscall.c:
char *str[]={[SYS_fork] "syscall fork",[SYS_exit] "syscall exit",[SYS_wait] "syscall wait",[SYS_pipe] "syscall pipe",[SYS_read] "syscall read",[SYS_kill] "syscall kill",[SYS_exec] "syscall exec",[SYS_fstat] "syscall fstat",[SYS_chdir] "syscall chdir",[SYS_dup] "syscall dup",[SYS_getpid] "syscall getpid",[SYS_sbrk] "syscall sbrk",[SYS_sleep] "syscall sleep",[SYS_uptime] "syscall uptime",[SYS_open] "syscall open",[SYS_write] "syscall write",[SYS_mknod] "syscall mknod",[SYS_unlink] "syscall unlink",[SYS_link] "syscall link",[SYS_mkdir] "syscall mkdir",[SYS_close] "syscall close",[SYS_trace] "syscall trace",};voidsyscall(void){int num;struct proc *p = myproc();num = p->trapframe->a7; //系统调用号if(num > 0 && num < NELEM(syscalls) && syscalls[num]) {// Use num to lookup the system call function for num, call it,// and store its return value in p->trapframe->a0p->trapframe->a0 = syscalls[num](); //系统调用的返回值if((p->mask >> num) & 1) //若该系统调用被跟踪printf("%d: %s -> %d\n",p->pid,str[num],p->trapframe->a0);//输出信息} else {printf("%d %s: unknown sys call %d\n",p->pid, p->name, num);p->trapframe->a0 = -1;}}
描述一下整个系统调用的流程:核心点就在于可以通过usys.pl文件里对系统调用的定义,使得可以在用户空间调用系统调用。
在用户空间的trace.c定义了trace调用,然后通过ECALL指令触发向内核态的切换,将对应的系统调用号和参数存入寄存器,切换至内核态之后由syscall函数对调用进行响应,然后调用对应的系统调用。处理完成之后将结果返还给用户空间,再切换回用户态,完成一次系统调用。
sysinfo
整体流程和trace差不多,获取非unused的进程数,核心就是遍历进程结构体数组proc,并判断其元素的state。
uint64 get_used_proc(){struct proc *p;uint64 n = 0;for(p = proc; p < &proc[NPROC]; p++) {if(p->state != UNUSED) n++;}return n;}
获取空闲内存,通过查看文件kernel/kalloc.c可知每个物理内存页的单位是PGSIZE=4096字节, 以一个单链表的形式管理空闲内存页,我们只需遍历该单链表获取空闲页数,每页计一个PGSIZE即可。
uint64 get_free_memory(){uint64 n=0;struct run* r = kmem.freelist;while(r){r=r->next;n += PGSIZE;}return n;}
将这些信息填入sysinfo结构体,然后返还给用户空间。
uint64 sys_sysinfo(){uint64 st;argaddr(0, &st);//获取从用户空间传入的指针。struct sysinfo p;//将信息存在结构体中p.nproc = get_used_proc();p.freemem = get_free_memory();if(copyout(myproc()->pagetable, st, (char *)&p, sizeof(p)) < 0)//拷贝回用户空间return -1;return 0;}