XV6 0x2
lab2
TOPIC:
ISOLATION:将资源抽象为服务。我们需要控制应用权限,防止应用程序间的相互影响,定期让应用程序让出cpu…
KERNEL/USER MODE:隔离操作系统内核和用户应用程序。
SYSTEM CALL:从用户态转到内核态。通过
ECALL指令
并传入系统调用号
实现。
RISCV模式:用户模式,监督者模式,机器模式。
硬件对隔离的支持
- 硬件可以通过寄存器的一个位判断当前模式,以允许执行特权指令。
- 硬件通过虚拟内存限制进程可以访问的内存空间。
宏内核与微内核:
宏内核:将所有操作系统服务都运行在内核模式中。
- 优点:较好的性能,如应用程序与文件系统交互,只需进入一次内核。
- 缺点:易产生BUG,且一旦产生bug极可能导致内核崩溃。
微内核:内核模式只提供基础支持,将文件系统等功能作为普通应用程序运行
- 优点:较少的BUG,某功能崩溃可尝试重启。
- 缺点:性能欠佳,如应用程序与文件系统交互,应用程序需要先跳转进内核,再从内核跳转进文件系统用户程序。
gdb配置
首先将gdb配置输出到~/.gdbinit。
1 | echo "add-auto-load-safe-path /home/randolfluo/Desktop/xv6/xv6-labs-2020/.gdbinit " >> ~/.gdbinit |
然后tmux打开两个窗口,分别运行xv6和gdb即可进行远程调试。
1 | [0]:make qemu-gdb |
System calls
下面两个实验主要是让我们熟悉系统调用的过程:
System call tracing
在usyc.pl添加进入syscall的入口,编译时会将该入口编译为汇编指令
1 | entry("trace"); |
在user.h添加系统调用
1 | int trace(int); |
在sysproc.c添加,参数获取用argint函数,获取保存在trapframe
寄存器的值。
1 | uint64 |
在proc.h中的进程结构体添加掩码,记录该进程是否需要追踪
1 | struct proc { |
在syscall.h添加宏定义,定义系统调用号
1 |
在syscall.c添加对应输出函数。
1 |
|
在proc.c中的fork()函数设置子进程继承掩码
1 | int fork(voide) |
最后在makefile中将trace添加进编译选项。
1 | $U/_trace\ |
通过实验总结,我们可以发现trace实际是通过设置进程结构体中的标志位来标记进程中需要跟踪的系统调用,并通过在fork()中继承标记位来实现对子进程的追踪。最后在每次调用syscall检查对应掩码输出调用信息。
Sysinfo
和trace差不多的修改程序,但是我们需要自己创建两个函数
先修改系统调用入口usys.pl
1 | entry("sysinfo"); |
在user.h添加添加声明,提供用户接口
1 | struct sysinfo; |
在syscall.h添加系统调用号
1 |
在syscall.c添加SYS_sysinfo
到trace追踪里
1 | extern uint64 sys_sysinfo(void); |
在proc.c添加获取进程数
1 | int |
在kalloc.c获取空闲内存大小,由于空闲列表是通过链表实现的且每次分配内存都为PGSIZE
,我们可以计算空闲块数*PGSIZE
得到。
1 | int |
在defs.h添加上述函数,方便其他系统模块调用。
1 | // kalloc.c |
在sysproc.c添加sys_sysinfo
系统调用
1 |
|
别忘了在makefile添加测试文件
1 | $U/_sysinfotest\ |