这学期选了cs555 Advanced Operating System, 据说这课load比较大, 但对于头铁又放飞gpa的我来说根本就无所畏惧(笑). 教授是个大佬,每节课带着读一篇system方向的经典paper,讲的挺好. 然后实验是搬运的mit 6.828, 网上资源很多,感觉工作量会比较大,这学期秃头预定😂

这系列我觉得我是不会弃坑的(真香警告)… 这里就记录一下码代码的大致心得以及我觉得重点的地方吧,抄答案还是另寻别处吧,以下正文

part 0: setup envoronment

把课程代码clone下来之后编译, 我用的MacOS, 版本10.13 high Sierra, 总结就是Homebrew大法好, brew装了i386-elf-gcc, i386-elf-gdb, i386-elf-binutils三件套,再改改makefile就完事了,做完lab1现在没发现啥问题…

part 1: PC Bootstrap

PC的物理地址空间大致长这样:

 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
+------------------+  <- 0xFFFFFFFF (4GB)
|      32-bit      |
|  memory mapped   |
|     devices      |
|                  |
/\/\/\/\/\/\/\/\/\/\

/\/\/\/\/\/\/\/\/\/\
|                  |
|      Unused      |
|                  |
+------------------+  <- depends on amount of RAM
|                  |
|                  |
| Extended Memory  |
|                  |
|                  |
+------------------+  <- 0x00100000 (1MB)
|     BIOS ROM     |
+------------------+  <- 0x000F0000 (960KB)
|  16-bit devices, |
|  expansion ROMs  |
+------------------+  <- 0x000C0000 (768KB)
|   VGA Display    |
+------------------+  <- 0x000A0000 (640KB)
|                  |
|    Low Memory    |
|                  |
+------------------+  <- 0x00000000

有个问题:为什么16位的PC能寻址1MB(2^20)的地址空间呢?

早期16位PC是基于intel 8088处理器, 而8088/8086这些16位的处理器是靠分段(segmentation)来寻址20位地址空间的.计算公式是physical address = (16 * segment address + offset)

8088的BIOS设计放在0x000f0000-0x000fffff处, 当处理器启动的时候,进入实模式,起始地址为0xffff0 (CS:IP = 0xf000:0xfff0)

BIOS运行时会建立中断描述符表(interrupt descriptor table)以及初始化一系列硬件设备,之后搜寻可启动的设备(CD-ROM之类的),找到之后BIOS从设备中读取引导程序(boot loader)然后将控制权转交给它.


part 2: The Boot Loader

PC硬盘被划分为许多512byte大小的扇区,一个可以启动的磁盘的第一个扇区就是引导程序,BIOS会将其读入0x7c00~0x7dff的地址中,之后执行jmp指令跳转到0x7c00处,将控制转交给引导程序.至于为什么是0x7c00,这是历史原因了,详情参见wiki

boot loader 主要做这么些事:

  • 将处理器从实模式切换到32位保护模式
  • 将整个kernel读入memory中
  • 控制转交给kernel

这里是通过设置CR0寄存器来切换到保护模式的

kernel是ELF格式的,我们主要关注这几个部分:

  • .text: 程序的可执行指令
  • .rodata: read-only data
  • .data: 程序初始化的数据 (像int x = 0之类的)

可以使用objdump来查看详细信息

其中.text部分的link addressload address需要注意:

  • link address: 指令运行时的地址,一般是虚拟地址
  • load address: 指令load进内存的地址

可以发现实模式下这两地址是一样的,因为并没有开启虚存,指令执行的时候就没有地址映射关系

看一下main.c里面,kernel的入口是((void (*)(void)) (ELFHDR->e_entry))();在boot.asm中可以看到对应boot loader执行的最后一条指令是7d73: ff 15 18 00 01 00 call *0x10018. 之后控制交给kernel,从entry.S开始运行,第一条指令是f010000c: 66 c7 05 72 04 00 00 movw $0x1234,0x472.


part 3: The Kernel

这里JOS其实有两个地址映射的关系,在kern/entrypgdir.c里面可以看到:

  • Map VA’s [0, 4MB) to PA’s [0, 4MB)
  • Map VA’s [KERNBASE, KERNBASE+4MB) to PA’s [0, 4MB) 其中第一个映射只是为了entry.S中的指令能正确执行,过了entry.S就没用了,具体可以看代码和注释

format printing这个只要看一下kern/printf.c, lib/printfmt.c, kern/console.c这三个地方就清楚了. 这里有个challenge是在控制台输出彩色文字,一个思路是用ANSI escape sequence,也就是类似\u001b[31mHelloWorld\u001b[0m这样的输出. 还有一个思路是可以参考一下VGA text buffer的格式,修改前景色那几位就行了.

stack可以看一下kernel.asm里面test_backtrace对esp,ebp的操作.

stabs可以看一下这里然后看看生成的.s文件大概就能看出stabs里面都存了啥东西,就比较好理解kdebug.c了