diff --git a/thu_os/chp1.md b/thu_os/chp1.md index b7dd5bd..9ecbe08 100644 --- a/thu_os/chp1.md +++ b/thu_os/chp1.md @@ -23,7 +23,7 @@ BIOS -> 加载程序(Bootloader) -> 操作系统 + 这是因为操作系统的种类复杂,有许多不同的操作系统,他们各自具有不同的文件系统。BIOS的内容是出厂固化的,不可能使之可以分辨所有的操作系统。让BIOS的那点程序分辨不同操作系统的文件系统,并将其加载到内存中,会使BIOS的设计非常复杂,以至于BIOS自己就成为了一个操作系统。 -+ 而Bootloader并不属于任何的操作系统,不属于任何的磁盘扇区,而是存在于一个叫引导扇区的地方,其中,一个扇区大小512字节,因此Bootloader也是512字节大小。它可以具有独立于操作系统的编码方式,因此可以被BIOS识别。故可以先将Bootloader读入内存,再又Bootloader加载操作系统。 ++ 而Bootloader并不属于任何的操作系统,不属于任何的磁盘扇区,而是存在于一个叫引导扇区的地方,其中,一个扇区大小512字节,因此Bootloader也是512字节大小。它可以具有独立于操作系统的编码方式,因此可以被BIOS识别。故可以先将Bootloader读入内存,再由Bootloader加载操作系统。 此外,我们看到,BIOS在加载Bootloader的过程中,已经有用到一些操作系统的功能,他们包括 diff --git a/thu_os/lab1_report.md b/thu_os/lab1_report.md index 5a9e702..2220f47 100644 --- a/thu_os/lab1_report.md +++ b/thu_os/lab1_report.md @@ -331,7 +331,7 @@ seta20.2: 保护模式就不一样了,因为地址空间已经变成了32位,而段寄存器的长度还是16位,直接用段寄存器的值来索引内存的某一个段感觉有点寒碜啊。这时候,段寄存器的值已经不是某一个地址了,而是用来索引段表中某一项的段选择子(segment selector) -我们知道,段表的结构是一个线性表,里面每一项包括8字节,其中前32位指令该段在内存中的起始地址。段表中至多可以有8192(2^13)项,也就是允许同时存在8192个段。此时,CS寄存器中的值只是对段表这个线性表的一个索引,表示当前选择的是段表中的第几个段。 +我们知道,段表的结构是一个线性表,里面每一项包括8字节,其中32位指示该段在内存中的起始地址。段表中至多可以有8192(2^13)项,也就是允许同时存在8192个段。此时,CS寄存器中的值只是对段表这个线性表的一个索引,表示当前选择的是段表中的第几个段。 由于至多有8192个段,因此可以只用CS中的高13位,就足以指定段表中的任意一个段。还有低3位是用来表示一些控制信息: @@ -435,7 +435,7 @@ protcseg: # Set up the protected-mode data segment registers ``` -其中,`$PROT_MODE_CSEG`就是段表中代码段的偏移,即0x8, `$protcseg`则为下一条指令的偏移地址。这样,Bootloader才算完全实现了从实模式进入到保护模式。 +其中,`$PROT_MODE_CSEG`就是段表中代码段的偏移,即0x8, `$protcseg`则为下一条指令的偏移地址。这样,Bootloader才算完全实现了从实模式进入到保护模式。那么,这条长跳转指令本身是如何被找到的呢?我的想法是由于CPU的流水线机制,导致在进入保护模式前这条指令被预取了。 ## 练习4:分析bootloader加载ELF格式的OS的过程。 @@ -745,7 +745,16 @@ asm volatile("movl (%0), %%ebp": "r"(ebp)) 这个思路我本来觉得挺好的,但是在更新`ebp`后我之前定义的局部变量就都失效了,就是说具有一个未定义的值。因此,也印证了各个变量的确是存储在当前函数的局部堆栈里面,一旦修改了`ebp`,就索引不到这些变量了。 + 还应该注意到最后两句内联汇编的次序。之前也说过,`ebp + 4`的值并不是与当前`ebp`对应的`eip`,而是上层函数的返回地址。因此,需要先读取这个`eip`,再更新`ebp`,才能使两者对应起来。 + 感觉题目给的示例输出有问题啊,似乎是直接指定了函数的参数固定输出四个。而我的代码是根据`debuginfo`给出的函数参数个数信息,逐个进行打印的。 -+ 最后一行的数值?至今没看懂是什么意思。 ++ 关于最后最后一行的数值,可以追溯到`bootasm.S`中对`esp`和`ebp`的初始化工作: + +```asm + # Set up the stack pointer and call into C. The stack region is from 0--start(0x7c00) + movl $0x0, %ebp + movl $start, %esp + call bootmain +``` + +可以看到,初始的`esp`被设置为了`bootloaded`的起始地址,亦即`0x7c00`。最后进行了一次函数调用`call bootmain`,首先是硬件将`eip`压栈,此时`esp = 0x7bfb`,随后是编译器添加的将`ebp`压栈,然后令`ebp = esp`,此时`ebp = esp = 0x7bf8`,这也就是函数调用堆栈中的第一个`ebp`,因此对函数调用过程的追踪终止于此,可以看到`ebp`的值和这里的分析是一致的。 ## 练习6:完善中断初始化和处理 (需要编程) @@ -765,7 +774,7 @@ asm volatile("movl (%0), %%ebp": "r"(ebp)) 为了要能描述中断服务程序的入口地址,门描述符显然应该具有 -+ 段描述符,2个字节。这样,查到段描述符后,显然还需要查段表,才能获得入口程序的物理地址。 ++ 段选择子,2个字节。这样,查到段选择子后,显然还需要查段表,才能获得入口程序的物理地址。 + 偏移量,4个字节 除此以外,还需要一些控制字,来描述优先级,中断类型(硬中断,异常,陷入)等信息。其具体结构如下: @@ -947,4 +956,4 @@ trap_dispatch(struct trapframe *tf) { 可以看到,在`trap_dispatch`里面才是进行实质的中断处理,主要是判断当前中断的中断号(存储在`trapframe`结构体中),我觉得应该可以用其他信息,来进行中断服务例程。 -关于这个程序,唯一想说的还是那个全局变量`ticks_count`,我将它定义在了`init.c`里面,但是总感觉不够优雅,我也不知道应该怎么优雅... +关于这个程序,唯一想说的还是那个全局变量`ticks_count`,我将它定义在了`init.c`里面,但是总感觉不够优雅,我也不知道应该怎么优雅...结果后来发现老师已经定义好了一个`ticks`全局变量。