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 pointermovl    $(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文件中,下面的这些代码是来实现这个机制的:

1   movl    $(RELOC(entry_pgdir)), %eax
2   movl    %eax, %cr3
3   movl    %cr0, %eax
4   orl    $(CR0_PE|CR0_PG|CR0_WP), %eax
5   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]范围内。

1     mov    $relocated, %eax
2     jmp    *%eax

  其中在实际调试时,这两条指令如下:

  

  可见relocated的值为0xf010002f。此时分页系统会把这个虚拟地址,转换为真实的物理地址。

  

  接下来就到了最关键的两句指令:

  

1     movl    $0x0,%ebp            # nuke frame pointer
2     movl    $(bootstacktop),%esp
3     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

  

  

转载于:https://www.cnblogs.com/fatsheep9146/p/5079177.html

MIT 6.828 JOS学习笔记12 Exercise 1.9相关推荐

  1. MIT 6.828 JOS学习笔记17. Lab 3.1 Part A User Environments

    Introduction 在这个实验中,我们将实现操作系统的一些基本功能,来实现用户环境下的进程的正常运行.你将会加强JOS内核的功能,为它增添一些重要的数据结构,用来记录用户进程环境的一些信息:创建 ...

  2. Linux学习笔记12——配置ftp、squid、Tomcat、Samba、MySQL主从

    Linux学习笔记12 Linux学习笔记12 配置FTP服务 配置pure-ftpd 开机启动 上传下载文件 配置vsftpd CentOS 70安装配置Vsftp服务器 搭好vsftp之后出现55 ...

  3. golang学习笔记12 beego table name `xxx` repeat register, must be unique 错误问题

    golang学习笔记12 beego table name `xxx` repeat register, must be unique 错误问题 今天测试了重新建一个项目生成新的表,然后复制到旧的项目 ...

  4. HALCON 20.11:深度学习笔记(12)---语义分割

    HALCON 20.11:深度学习笔记(12)--- 语义分割 HALCON 20.11.0.0中,实现了深度学习方法. 本章解释了如何使用基于深度学习的语义分割,包括训练和推理阶段. 通过语义分割, ...

  5. 台大李宏毅Machine Learning 2017Fall学习笔记 (12)Why Deep?

    台大李宏毅Machine Learning 2017Fall学习笔记 (12)Why Deep? 本博客整理自: http://blog.csdn.net/xzy_thu/article/detail ...

  6. Kotlin学习笔记12——数据类和密封类

    Kotlin学习笔记12--数据类和密封类 前言 数据类 在类体中声明的属性 复制 componentN 解构声明 密封类 尾巴 前言 上一篇,我们学习了Kotlin中的拓展,今天继续来学习Kotli ...

  7. R语言小白学习笔记12—概率分布

    R语言小白学习笔记12-概率分布 笔记链接 学习笔记12-概率分布 12.1 正态分布 12.2 二项分布 12.3 泊松分布 12.4 其他分布 笔记链接 学习笔记1-R语言基础. 学习笔记2-高级 ...

  8. ros学习笔记12——python实现发布和接收ros topic

    ros学习笔记12--python实现发布和接收ros topic 一.简单demo 1.工作空间是存放工程开发的相关文件的文件夹 2.创作工作空间指令 3 .创建功能包 4. 创建Topic的订阅发 ...

  9. 【计算机网络学习笔记12】交换技术(上)

    [计算机网络学习笔记12]交换技术(上) 经典局域网的交换技术 概念 以太网是由Xerox公司创建并由Xerox.intel和DEC公司联合开发的基带局域网规范,是当今现有局域网采用的最通用的通信协议 ...

最新文章

  1. 【GStreamer】gstreamer工具详解之:gst-launch-1.0
  2. 智能合约的核心思想、语法重点、编程模式、示例、规范及架构
  3. C++ STL之vector常用指令
  4. 百度定位sdk使用说明
  5. c++ string 头文件_“延期不延学” 第25期 | C++篇 | C/C++常用函数
  6. python 材料科学与工程专业_2020年最全的python的就业方向+清华计算机社流出上千集编程资料...
  7. mint-UI引用后样式不对
  8. socket编程 (PHP实现)
  9. 【MOSS】SPListItems操作
  10. 信息学奥赛一本通(C++)在线评测系统——基础(一)C++语言——1075:药房管理
  11. 中兴a2018拆机图片_中兴天机拆机步骤详解【图文】
  12. 学霸女神冯净冰:我是如何从复旦走到诺奖获得者经济学大会的
  13. php导出csv带图片,PHP导出CSV文件:刚测试过,这个导出CSV可以
  14. 探索图神经网络的网络架构和训练方法
  15. 虹软人脸识别SDK - Java服务端的那些事
  16. sqlite3数据存储最多存储多少条数据?达到上限如何处理?_在线公开课 | 在数据爆炸的当下,教你设计一个能实现9个9数据可靠性的存储系统...
  17. Atitit.二维码功能的设计实践 attilax 总结
  18. 中国移动宽带密码重置方法
  19. awk oracle,工具: ass109.awk 分析 Oracle 的跟踪文件
  20. MACOS PowerPoint导出指定分辨率的图片

热门文章

  1. 强制杀oracle进程
  2. git config配置文件
  3. oracle学习笔记一
  4. catia高级技巧54条1.0
  5. 【数据平台】pandas按条件去重
  6. (转载)安全漏洞概念及分类
  7. 长沙哪招jaVa后端开发人才_求职:Java后台开发-何柄融-湖南大学
  8. 企业千人千面管理模式_零售企业该如何打造“千人千面”的差异化营销?
  9. ES5新增的方法——数组的方法
  10. 冒泡排序用c语言实现