1.系统理解

第二章

2.1、xv6代码结构

代码主要有三个部分组成:

  1. kernel: 我们可以ls kernel,里面包含了所有的内核文件。因为XV6是一个宏内核结构,这里所有的文件会被编译成一个叫做kernel的二进制文件,然后这个二进制文件会被运行在kernel mode中。
  2. use: 基本上是运行在user mode的程序。这也是为什么一个目录称为kernel,另一个目录称为user的原因。
  3. mkfs: 它会创建一个空的文件镜像,我们会将这个镜像存在磁盘上,这样我们就可以直接使用一个空的文件系统。

2.2、xv6如何切换不同模式(user/kernel..)

2.3、xv6启动流程

entry.S开始启动,在末尾跳转到start.c

start.c主要完成以下功能:

  1. 在寄存器mstatus中把之前的特权模式设置为监督者模式

  2. 通过把main的地址写入寄存器mepc来设置返回地址为main

  3. 通过在页表寄存器satp中写入0来禁止监督者模式下的虚拟地址转换

  4. 把所有中断和异常委托给监督者模式

  5. 对时钟芯片进行编程以产生时钟中断

  6. 在完成这些工作后,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 页表工作流程总结

  1. 三级页表

  2. 同一个物理页可映射到多个虚拟地址

  3. 将根页表物理地址放到satp寄存器中供cpu翻译地址使用

  4. 内核和每个进程都有自己的页表

3.2 地址空间

每一页4KB,需要12位进行索引

虚拟地址

image-20220921014023506

虚拟地址64位,高25位不使用,低12位时页内索引,中间27位索引三级页表,每一级页表使用9位,所以每级页表最多\(2^9 = 512\)个页表项,又因为页大小为4KB = \(2^{12}\)B,所以每一个页表项大小为\(2^3B=8B=64bit\)

物理地址

内核页表

image-20220921022817320

进程页表

image-20220922201251797
进程页表和虚拟地址空间之间的关系
每个进程的页表是何时被创建的
虚拟地址空间何时被创建
那些事是CPU指令完成的,那些事是操作系统完成的?
posted @ 2024-04-21 00:18  INnoVation-V2  阅读(3)  评论(0编辑  收藏  举报