update lab1_report, solve some former problems.
This commit is contained in:
@@ -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的过程中,已经有用到一些操作系统的功能,他们包括
|
||||
|
||||
|
||||
@@ -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`全局变量。
|
||||
|
||||
Reference in New Issue
Block a user