lab2 TOPIC:
ISOLATION:将资源抽象为服务。我们需要控制应用权限,防止应用程序间的相互影响,定期让应用程序让出cpu…
KERNEL/USER MODE:隔离操作系统内核和用户应用程序。
SYSTEM CALL:从用户态转到内核态。通过ECALL指令并传入系统调用号实现。
RISCV模式:用户模式,监督者模式,机器模式。
硬件对隔离的支持
硬件可以通过寄存器的一个位判断当前模式,以允许执行特权指令。
硬件通过虚拟内存限制进程可以访问的内存空间。
宏内核与微内核:
gdb配置 首先将gdb配置输出到~/.gdbinit。
1 2 echo "add-auto-load-safe-path /home/randolfluo/Desktop/xv6/xv6-labs-2020/.gdbinit " >> ~/.gdbinitgdb-multiarch
然后tmux打开两个窗口,分别运行xv6和gdb即可进行远程调试。
1 2 [0]:make qemu-gdb [1]:gdb-multiarch
System calls 下面两个实验主要是让我们熟悉系统调用的过程:
System call tracing 在usyc.pl添加进入syscall的入口,编译时会将该入口编译为汇编指令
在user.h添加系统调用
在sysproc.c添加,参数获取用argint函数,获取保存在trapframe寄存器的值。
1 2 3 4 5 6 7 8 uint64 sys_trace (void ) { int n; argint(0 ,&n); myproc()->mask = n; return 0 ; }
在proc.h中的进程结构体添加掩码,记录该进程是否需要追踪
1 2 3 4 struct proc { ... int mask; }
在syscall.h添加宏定义,定义系统调用号
在syscall.c添加对应输出函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 extern uint64 sys_trace (void ) ;static uint64 (*syscalls[]) (void ) = { ...... [SYS_trace] sys_trace, }; char *sys_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" , }; if (num > 0 && num < NELEM(syscalls) && syscalls[num]) { p->trapframe->a0 = syscalls[num](); if ((1 << num) & p->mask) printf ("%d: syscall %s -> %d\n" , p->pid, sys_name[num], p->trapframe->a0); } else {
在proc.c中的fork()函数设置子进程继承掩码
1 2 3 4 5 6 int fork (voide) { np->trapframe->a0 = 0 ; np->mask = p->mask; }
最后在makefile中将trace添加进编译选项。
通过实验总结,我们可以发现trace实际是通过设置进程结构体中的标志位来标记进程中需要跟踪的系统调用,并通过在fork()中继承标记位来实现对子进程的追踪。最后在每次调用syscall检查对应掩码输出调用信息。
Sysinfo 和trace差不多的修改程序,但是我们需要自己创建两个函数
先修改系统调用入口usys.pl
在user.h添加添加声明,提供用户接口
1 2 struct sysinfo ;int sysinfo (struct sysinfo *) ;
在syscall.h添加系统调用号
在syscall.c添加SYS_sysinfo到trace追踪里
1 2 3 4 5 6 7 8 9 10 11 12 13 extern uint64 sys_sysinfo (void ) ;static uint64 (*syscalls[]) (void ) = {.... [SYS_sysinfo] sys_sysinfo, }; char *sys_name[]={ ... [SYS_sysinfo] "sysinfo" , };
在proc.c添加获取进程数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 int n_proc_num (void ) { struct proc *p ; int n = 0 ; for (p = proc; p < &proc[NPROC]; p++) { acquire(&p->lock); if (p->state != UNUSED) n++; release(&p->lock); } return n; }
在kalloc.c获取空闲内存大小,由于空闲列表是通过链表实现的且每次分配内存都为PGSIZE,我们可以计算空闲块数*PGSIZE得到。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 int free_mem (void ) { uint64 n=0 ; struct run *r ; acquire(&kmem.lock); r = kmem.freelist; while (r) { n += PGSIZE; r = r->next; } release(&kmem.lock); return n; }
在defs.h添加上述函数,方便其他系统模块调用。
1 2 3 4 5 6 int free_mem (void ) ;int n_proc_num (void ) ;
在sysproc.c添加sys_sysinfo系统调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include "sysinfo.h" ... uint64 sys_sysinfo (void ) { struct proc *p = myproc(); uint64 info; argaddr(0 , &info); struct sysinfo sys_info ; sys_info.freemem = free_mem(); sys_info.nproc = n_proc_num(); if (copyout(p->pagetable, info, (char *)&sys_info, sizeof (sys_info)) < 0 ) return -1 ; return 0 ; }
别忘了在makefile添加测试文件