1.系统理解
第二章
2.1、xv6代码结构
代码主要有三个部分组成:
kernel: 我们可以ls kernel,里面包含了所有的内核文件。因为XV6是一个宏内核结构,这里所有的文件会被编译成一个叫做kernel的二进制文件,然后这个二进制文件会被运行在kernel mode中。use: 基本上是运行在user mode的程序。这也是为什么一个目录称为kernel,另一个目录称为user的原因。mkfs: 它会创建一个空的文件镜像,我们会将这个镜像存在磁盘上,这样我们就可以直接使用一个空的文件系统。
2.2、xv6如何切换不同模式(user/kernel..)
2.3、xv6启动流程
从entry.S开始启动,在末尾跳转到start.c,
start.c主要完成以下功能:
-
在寄存器
mstatus中把之前的特权模式设置为监督者模式 -
通过把
main的地址写入寄存器mepc来设置返回地址为main, -
通过在页表寄存器
satp中写入0来禁止监督者模式下的虚拟地址转换 -
把所有中断和异常委托给监督者模式
-
对时钟芯片进行编程以产生时钟中断
-
在完成这些工作后,
start通过调用mret"返回"到监督者模式。之后程序会跳转到main(kernel/main.c:11)
main.c中完成以下设置:
consoleinit();
printfinit();
printf("\n");
printf("xv6 kernel is booting\n");
printf("\n");
kinit(); // physical page allocator
kvminit(); // create kernel page table
kvminithart(); // turn on paging
procinit(); // process table
trapinit(); // trap vectors
trapinithart(); // install kernel trap vector
plicinit(); // set up interrupt controller
plicinithart(); // ask PLIC for device interrupts
binit(); // buffer cache
iinit(); // inode table
fileinit(); // file table
virtio_disk_init(); // emulated hard disk
userinit(); // first user process
__sync_synchronize();
之后跳转到userinit()
通过userinit()中的initcode.s跳转到user/init.c,(initcode是一段翻译好的机器码,汇编格式见user/initcode.S)
init.c中调用执行sh.c,启动完成,
2.4 调用系统调用的流程
在用户态中,所有的系统接口都定义在user.h中,通过usys.pl将头文件中的系统接口与真正的系统接口实现连接起来,内核态的系统调用接口定义在syscall.h中,具体实现分布在内核各个文件中,通过数组下标索引在syscall.c中引用和调用
第三章
3.1 页表工作流程总结
-
三级页表
-
同一个物理页可映射到多个虚拟地址
-
将根页表物理地址放到satp寄存器中供cpu翻译地址使用
-
内核和每个进程都有自己的页表
3.2 地址空间
每一页4KB,需要12位进行索引
虚拟地址
虚拟地址64位,高25位不使用,低12位时页内索引,中间27位索引三级页表,每一级页表使用9位,所以每级页表最多\(2^9 = 512\)个页表项,又因为页大小为4KB = \(2^{12}\)B,所以每一个页表项大小为\(2^3B=8B=64bit\),
物理地址
内核页表
进程页表
进程页表和虚拟地址空间之间的关系
每个进程的页表是何时被创建的
虚拟地址空间何时被创建
那些事是CPU指令完成的,那些事是操作系统完成的?

浙公网安备 33010602011771号