MIT 6.828 JOS学习笔记12 Exercise 1.9
Lab 1中Exercise 9的解答报告
Exercise 1.9:
判断一下操作系统内核是从哪条指令开始初始化它的堆栈空间的,以及这个堆栈坐落在内存的哪个地方?内核是如何给它的堆栈保留一块内存空间的?堆栈指针又是指向这块被保留的区域的哪一端的呢?
答:
1. 首先需要判断操作系统内核是从哪条指令开始初始化它的堆栈空间的。
前面已经分析过boot.S和main.c文件的运行过程,这个文件中的代码是PC启动后,BIOS运行完成后,首先执行的两部分代码。但是它们并不属于操作系统的内核。当main.c文件中的bootmain函数运行到最后时,它执行的最后一条指令就是跳转到entry.S文件中的entry地址处。此时控制权已经被转交给了entry.S。
在跳转到entry之前,并没有对%esp,%ebp寄存器的内容进行修改,可见在bootmain中并没有初始化堆栈空间的语句。
下面进入entry.S,在entry.S中我们可以看到它最后一条指令是要调用i386_init()子程序。这个子程序位于init.c文件之中。在这个程序中已经开始对操作系统进行一些初始化工作,并且自重进入mointor函数。可见到i386_init子程序时,内核的堆栈应该已经设置好了。所以设置内核堆栈的指令就应该是entry.S中位于 call i386_init 指令之前的两条语句:
movl $0x0,%ebp # nuke frame pointer
movl $(bootstacktop),%esp
这两条指令修改了%ebp,%esp两个寄存器的值,而这两个寄存器的值是和堆栈息息相关的。
2. 这个堆栈坐落在内存的什么地方?
解答这个问题就需要我们好好分析一下entry.S文件了。最好的方法自然是阅读其源码,并且也要配合反汇编文件。
首先我们配置好一个qemu,gdb调试环境,现在我们首先在gdb中设置一个断点(指令: b *0x7d63),就设置到马上进入entry之前,然后一步步进行调试。

当我们准备运行entry.S中第一条指令 movx $0x1234, 0x472 时,指令地址是0x10000C,如上图所示。这个比较好理解,因为在bootmain里面,我们已经把操作系统的内核文件全部加载到物理内存0x100000处了。所以0x10000C是系统内核的第一条指令所在的物理地址处。
而当我们继续运行运行到jmp *%eax之后,后面的指令地址就都变化了,变换为:

图中的地址是0xf010002f,很明显这是一个虚拟地址,它的真实地址应该是0x0010002f,因为所有的内核代码都实际存放在这个内存区域中。之所以现在要把指令地址设置为0xf010002f,即把操作系统的代码的虚拟地址设置为从0xf0100000开始。目的就是能够让程序员在编程时,能够利用虚拟地址空间的低地址空间。如果它编写的程序调用了操作系统的代码,则操作系统代码的虚拟地址一定位于高地址空间0xf0100000处。这样非常有利于程序员写程序。
所以必须有一种机制能够实现,即便程序员在程序中指定的操作系统的代码的虚拟地址在0xf0100000高地址空间,但是我们这个机制也能够把这个高地址转换为这个代码真实的在内存中的位置。比如上图中,我们想访问0xf010002f处的指令,这是个虚拟地址,当实际运行时,会有一种机制把这个地址转换为真实地址0x0010002f。
在entry.S文件中,下面的这些代码是来实现这个机制的:
movl $(RELOC(entry_pgdir)), %eax
movl %eax, %cr3
movl %cr0, %eax
orl $(CR0_PE|CR0_PG|CR0_WP), %eax
movl %eax, %cr0
这个机制的实现方式是通过写了一个c语言的页表,entry_pgdir,这个手写的页表可以自动的把[0xf0000000-0xf0400000]这4MB的虚拟地址空间映射为[0x00000000-0x00400000]的物理地址空间。可见这个页表的映射能力还是比较有限的,只能映射一个区域。对于当前执行的这些指令,这个映射空间就已经足够了。因为当前运行的是内核程序,他们的虚拟空间地址范围在[0xf0000000-0xf0400000]之内。但是当操作系统真正正常的运行起来的时候,这个映射就不够用了。必须采用更全面的,也就是在lab 2中要介绍的页表机制。所以当操作系统真正正常运行起来时,entry_pgdir这个页表将不会再使用。
现在依次分析上述语句,首先看第1句,它的功能是把entry_pgdir这个页表的起始物理地址送给%eax,这里RELOC宏的功能是计算输入参数的物理地址。
第2句,把entry_pgdir这个页表的起始地址传送给寄存器%cr3。
控制寄存器cr2和cr3都是和分页机制相关的寄存器。其中cr3寄存器存放页表的物理起始地址。
第3~5句,修改cr0寄存器的值,把cr0的PE位,PG位, WP位都置位1。其中PE位是启用保护标识位,如果被置1代表将会运行在保护模式下。PG位是分页标识位,如果这一位被置1,则代表开启了分页机制。WP位是写保护标识,如果被置位为1,则处理器会禁止超级用户程序向用户级只读页面执行写操作。
这条指令过后,就开始工作在具有分页机制的模式之下了。接下来的指令就可以指定[0xf0000000-0xf0400000]范围的指令了。
然后下面两条指令就把当前运行程序的地址空间提高到[0xf0000000-0xf0400000]范围内。
mov $relocated, %eax
jmp *%eax
其中在实际调试时,这两条指令如下:

可见relocated的值为0xf010002f。此时分页系统会把这个虚拟地址,转换为真实的物理地址。
接下来就到了最关键的两句指令:
movl $0x0,%ebp # nuke frame pointer
movl $(bootstacktop),%esp
call i386_init
这两个指令分别设置了%ebp,%esp两个寄存器的值。其中%ebp被修改为0。%esp则被修改为bootstacktop的值。这个值为0xf0110000。另外在entry.S的末尾还定义了一个值,bootstack。注意,在数据段中定义栈顶bootstacktop之前,首先分配了KSTKSIZE这么多的存储空间,专门用于堆栈,这个KSTKSIZE = 8 * PGSIZE = 8 * 4096 = 32KB。所以用于堆栈的地址空间为 0xf0108000-0xf0110000,其中栈顶指针指向0xf0110000. 那么这个堆栈实际坐落在内存的 0x00108000-0x00110000物理地址空间中。
3. 内核是如何给它的堆栈保留一块内存空间的?
其实就是通过刚刚分析的,在entry.S中的数据段里面声明一块大小为32Kb的空间作为堆栈使用。从而为内核保留了一块空间。
4. 堆栈指针又是指向这块被保留的区域的哪一端的呢?
堆栈由于是向下生长的,所以堆栈指针自然要指向最高地址了。最高地址就是我们之前看到的bootstacktop的值。所以将会把这个值赋给堆栈指针寄存器。
以上就是对Exercise 1.9的分析,欢迎大家提出问题或建议~
zzqwf12345@163.com
MIT 6.828 JOS学习笔记12 Exercise 1.9的更多相关文章
- MIT 6.828 JOS学习笔记11 Exercise 1.8
Exercise 1.8 我们丢弃了一小部分代码---即当我们在printf中指定输出"%o"格式的字符串,即八进制格式的代码.尝试去完成这部分程序. 解答: 在这个练 ...
- MIT 6.828 JOS学习笔记5. Exercise 1.3
Lab 1 Exercise 3 设置一个断点在地址0x7c00处,这是boot sector被加载的位置.然后让程序继续运行直到这个断点.跟踪/boot/boot.S文件的每一条指令,同时使用boo ...
- MIT 6.828 JOS学习笔记13 Exercise 1.10
Lab 1 Exercise 10 为了能够更好的了解在x86上的C程序调用过程的细节,我们首先找到在obj/kern/kern.asm中test_backtrace子程序的地址, 设置断点,并且探讨 ...
- MIT 6.828 JOS学习笔记8. Exercise 1.4
Lab 1 Exercise 4 阅读关于C语言的指针部分的知识.最好的参考书自然是"The C Programming Language". 阅读5.1到5.5节.然后下载poi ...
- MIT 6.828 JOS学习笔记9. Exercise 1.5
Lab 1 Exercise 5 再一次追踪一下boot loader的一开始的几句指令,找到第一条满足如下条件的指令处: 当我修改了boot loader的链接地址,这个指令就会出现错误. 找到这样 ...
- MIT 6.828 JOS学习笔记3. Exercise 1.2
这篇博文是对Lab 1中的Exercise 2的解答~ Lab 1 Exercise 2: 使用GDB的'si'命令,去追踪ROM BIOS几条指令,并且试图去猜测,它是在做什么.但是不需要把每个细节 ...
- MIT 6.828 JOS学习笔记2. Lab 1 Part 1.2: PC bootstrap
Lab 1 Part 1: PC bootstrap 我们继续~ PC机的物理地址空间 这一节我们将深入的探究到底PC是如何启动的.首先我们看一下通常一个PC的物理地址空间是如何布局的: ...
- MIT 6.828 JOS学习笔记0. 写在前面的话
0. 简介 操作系统是计算机科学中十分重要的一门基础学科,是一名计算机专业毕业生必须要具备的基础知识.但是在学习这门课时,如果仅仅把目光停留在课本上一些关于操作系统概念上的叙述,并不能对操作系统有着深 ...
- MIT 6.828 JOS学习笔记10. Lab 1 Part 3: The kernel
Lab 1 Part 3: The kernel 现在我们将开始具体讨论一下JOS内核了.就像boot loader一样,内核开始的时候也是一些汇编语句,用于设置一些东西,来保证C语言的程序能够正确的 ...
随机推荐
- 年月日与time的相互转换
年月日的转换 // 这里就是把时间格式化成你要的 SimpleDateFormat sdf = new SimpleDateFormat("yyyy");//将时间转换为年 Sim ...
- Android ActionBar 初探
1.指南,例子,个人感觉 首先上官网指南链接http://developer.android.com/guide/topics/ui/actionbar.html 参考了官网上的例子http://de ...
- 浅谈C++之冒泡排序、希尔排序、快速排序、插入排序、堆排序、基数排序性能对比分析之后续补充说明(有图有真相)
如果你觉得我的有些话有点唐突,你不理解可以想看看前一篇<C++之冒泡排序.希尔排序.快速排序.插入排序.堆排序.基数排序性能对比分析>. 这几天闲着没事就写了一篇<C++之冒泡排序. ...
- Web获取客户端物理MAC地址(ocx插件)
主要是通过ActiveX控件 从本地获取到MAC地址,传入到浏览器打开的网页中,再提交到服务器. 具体详解与步骤看文档中: 文件实例包下载 DotNetFX 文件夹附件文件:(可能安装时需用) dot ...
- JDBC值事务
事务的四大特性: 原子性, 一致性(比如说A给B转账,A转了之后B的账户增加了,两个都完成才叫一致性),隔离性(A给B转账,A给C转账,AB和AC并发是无关的),永久性(转账之后 不可能复原,就是说不 ...
- input输入时光标位置靠上问题解决
在css中如果我们定义了input高度在输入时会发现光标位置靠上了不在居中了,在Chrome浏览器中,当设置了line-height时,input无文字,光标高度与line-height一致:inpu ...
- CF Round #367 C题
题目 链接:http://codeforces.com/contest/706/problem/C 好像又是DP... dp[i][0]表示第i个字符串不翻转成字典序排列的花费,dp[i][1]表示第 ...
- 在Extjs中对日期的处理,以及在后端数据在SQL语句的判断处理
jsp页面可选择时间: { xtype : 'datefield', id : 'START_CREATION_DATE_', format : 'Y-m-d H:i:s', submitFormat ...
- iOS的一像素线
文/stark_yang(简书作者)原文链接:http://www.jianshu.com/p/b83dca88ef73著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”. 时常总结以前学过 ...
- 微信小程序导航:官方工具+精品教程+DEMO集合(1月7更新)
1:官方工具:https://mp.weixin.qq.com/debug/w ... tml?t=14764346784612:简易教程:https://mp.weixin.qq.com/debug ...