Lab2 - System calls
1. xv6 启动流程
当 RISC-V 计算机启动时,它初始化自己并运行存储在 ROM 中的引导加载程序。引导加载程序将 xv6 内核(镜像文件)加载到内存中指定位置 0x80000000,此时分页并未启用,虚拟地址直接映射到物理地址。借助 kernel.ld 链接脚本将下列代码放在指定位置,然后,在机器模式下,CPU 从 _entry 开始执行xv6。
1 | # qemu -kernel loads the kernel at 0x80000000 |
之所以将内核放置在 0x80000000 而不是 0x0,是因为地址范围 0x0 : 0x80000000 包含了 I / O 设备。_entry处的指令建立了一个栈(允许函数调用),使 xv6 可以运行 C 代码。xv6 在文件 start.c 中为初始栈 stack0 声明了空间。现在内核有了栈,开始时调用C代码。
start() 函数执行一些只允许在机器模式下执行的配置,然后切换到 supervisor 模式。为了进入 supervisor 模式,RISC-V 提供了 mret 指令。该指令通常用于从上一次调用从 supervisor 模式返回到机器模式。start() 不会从这样的调用中返回,它在寄存器 mstatus 中将之前的特权模式设置为 supervisor,通过将 main() 的地址写入寄存器 mepc 来将返回地址设置为 main,通过将页表寄存器 satp 写入 0 来禁用 supervisor 模式下的虚拟地址转换,并将所有中断和异常委托给 supervisor 模式。在进入 supervisor 模式之前,start 还要时钟芯片进行编程,以产生定时器中断。有了这个清理工作,通过调用 mret 开始“返回”到 supervisor 模式。
1 | // entry.S jumps here in machine mode on stack0. |
在main () 初始化几个设备和子系统之后,它调用 userinit() 创建第一个用户态进程。第一个进程执行一个 initcode.S,它调用了 exec 启动了 shell 来代替当前进程。
至此启动完成了。
1 | // start() jumps here in supervisor mode on all CPUs. |
1 | // a user program that calls exec("/init") |
1 | # Initial process that execs /init. |
2. 系统调用
3. make grade

4. 遗留问题
结构体在编译时可以只声明吗??
可以,不过当需要用到它的大小时,比如创建变量、sizeof 等等,就会报错(缺少信息,无法生成中间代码。
在声明用户态的 sysinfo() 时为什么需要前向声明 struct sysinfo ?
声明指等到链接时再找到具体定义,其实就相当于 #include 所需头文件的一部分,而不是全部导入,由于允许重复声明而不允许重复定义,这样做防止直接 #include 带来的重复定义、循环依赖等问题。
