MIT 6.828 JOS学习笔记7. Lab 1 Part 2.2: The Boot Loader

Lab 1 Part 2 The Boot Loader

Loading the Kernel

我们现在可以进一步的讨论一下boot loader中的C语言的部分,即boot/main.c。但是在我们分析之前,我们应该先回顾一些关于C语言的基础知识。



Exercise 4:

  阅读关于C语言的指针部分的知识。最好的参考书自然是"The C Programming Language"。

  阅读5.1到5.5节。然后下载pointers.c的代码,并且编译运行它,确保你理解在屏幕上打印出来的所有的值是怎么来的。尤其要重点理解第1行,第6行的指针地址是如何得到的,以及在第2行到第4行的值是如何得到的,还有为什么在第5行打印出来的值看起来像程序崩溃了。

  Waring:除非你已经对C语言相当了解,否则千万不要跳过这一个练习。

    这个练习的解答连接:http://www.cnblogs.com/fatsheep9146/p/5216735.html



  为了能够理解boot/main.c程序,你必须首先清楚什么是ELF文件。当你在编译并且链接了像JOS内核这样的C语言程序之后,编译器会把C语言源文件(.c后缀)转换为目标文件(.o后缀)。目标文件中包含的是机器直接能够执行的机器指令。链接器在把所有的目标文件组合成一个单独的二进制映像(binary image),比如obj/kern/kernel。这类二进制映像文件就是ELF格式的。

  在6.828中,你可以认为一个可以执行的ELF文件是由三大部分组成:一个是带有加载信息的文件头,然后紧跟着程序段表,然后紧跟着的就是几个程序段(program section)。其中每一个段都是一块连续的代码或者数据。它们在被运行时要首先被加载到内存中。boot loader的工作就是把它们加载到内存中。

  一个ELF文件,开始处是一个固定长度的ELF文件头,后面紧跟着一个程序段表,这个段表中列出了要加载到内存中的所有段。关于ELF文件头的格式在inc/elf.h文件中有声明。在6.828中我们对三个段非常感兴趣:

  * .text段:存放所有程序的可执行代码

  * .rodata段:存放所有只读数据的数据段,比如字符串常量。

  * .data段:存放所有被初始化过的数据段,比如有初始值的全局变量。

  当链接器在计算整个程序的内存布局时,它会为没有被初始化过的变量,比如int x;,在一个紧跟在.data段后的段,.bss段中保留它们的信息。C语言要求所有没有被初始化的变量值都为0。因而我们并不需要在ELF文件中存放这些变量的值,因为一定是0。因此链接器只是把这些变量的地址和大小存放在.bss段中。只有当程序装入内存后,由装入器为这些段赋予初值0。

  你可以通过下面的指令来考察JOS内核中所有段的名字,大小和地址。

     objdump -h obj/kern/kernel

  得到的结果如下图:

  

  在图中我们会发现这个可执行文件的所有段的信息,其中不仅仅包括我们之前提到的那四个段,还有一些其他的,他们主要用于存放一些debug信息等等。

  在每一个段中都有两个比较重要的字段,VMA(链接地址),LMA(加载地址)。其中加载地址代表的就是这个段被加载到内存中后,它所在的物理地址。链接地址则指的是这个段希望被存放到的逻辑地址。

  每一个ELF文件中都有一个Program Headers Table,用于指明ELF文件中哪些部分被加载到内存,以及被加载到内存中的地址。你可以通过输入下述指令来获取kernel的Program Headers Table的信息:

    objdump -x obj/kern/kernel

  

  其中Program Header中列出的是所有被加载到内存中的段的信息,这也是Program Headers Table的表项。每一个表项图中都把这个表项中涉及到的所有字段都列出来了。可见有一些段最后没有被加入到内存之中。在上图中,那些需要被加载到内存的段被标记为LOAD。

  BIOS通常会把boot sector加载到内存地址0x7c00处,这是boot sector的加载地址,也是boot sector的链接地址。我们可以通过boot/Makefrag文件中的-Ttext 0x7c00语句设置boot sector的链接地址,并且这个链接地址后来会被链接器所使用,保证链接器产生正确的代码。



  Exercise 5

    再一次追踪一下boot loader的一开始的几句指令,找到第一条满足如下条件的指令处:

    当我修改了boot loader的链接地址,这个指令就会出现错误。

    找到这样的指令后,把boot loader的链接地址修改一下,我们要在boot/Makefrag文件中修改它的链接地址,修改完成后运行 make clean, 然后通过make指令重新编译内核,再找到那条指令看看会发生什么。最后别忘了改回来。

  练习解答连接:http://www.cnblogs.com/fatsheep9146/p/5220004.html



  再让我们回顾一下内核的加载地址和链接地址。和boot loader不同,内核的这两个地址是不同的。内核告诉boot loader把它加载到低地址处(加载地址),但是它希望运行在高地址处(链接地址)。我们将在下一章仔细看这个问题。

  除了各个段的信息,在ELF头部中,还有一个非常重要的信息就是e_entry字段。这个字段存放的是这个可执行程序的执行入口处的链接地址。通过下面的指令你可以查看内核程序入口处。

  

   可见程序入口地址为0x0010000C,这个地址也和我们之前的分析相符合。



  Exercise 6

  在这个练习中,我们将尝试使用GDB的x命令(查看内存命令)。 x/Nx ADDR。这个指令将打印出从ADDR地址开始之后的N个字的内容。重启一下Qemu。在Bios进入boot loader之前,内存地址0x00100000处8个字的内容,然后进入boot loader运行到内核开始处停止,再看下这个地址处的值。为什么二者不同?第二次这个内存处所存放的值的含义是什么?

  解答:

    在进入boot loader之前,从内存地址0x00100000处开始之后8个字的内容为:

    

    在进入kernel那一刻之前,从内存地址0x00100000处开始之后8个字的内容为:

    

为什么会产生这种变化,因为bootmain函数在最后会把内核的各个程序段送入到内存地址0x00100000处,所以这里现在存放的就是内核的某一个段的内容,由于程序入口地址是0x0010000C,正好位于这个段中,所以可以推测,这里面存放的应该是指令段,即.text段的内容。


  

  那么到此为止Lab 1的Part 2部分的所有实验就都已经完成了。

  如果有问题和建议,欢迎骚扰

    [email protected]

  

时间: 2024-10-25 16:41:47

MIT 6.828 JOS学习笔记7. Lab 1 Part 2.2: The Boot Loader的相关文章

MIT 6.828 JOS学习笔记4. Lab 1 Part 2.1: The Boot Loader

Part 2: The Boot Loader 对于PC来说,软盘,硬盘都可以被划分为一个个大小为512字节的区域,叫做扇区.一个扇区是一次磁盘操作的最小粒度.每一次读取或者写入操作都必须是一个或多个扇区.如果一个磁盘是可以被用来启动操作系统的,就把这个磁盘的第一个扇区叫做启动扇区.这一部分介绍的boot loader程序就位于这个启动扇区之中.当BIOS找到一个可以启动的软盘或硬盘后,它就会把这512字节的启动扇区加载到内存地址0x7c00~0x7dff这个区域内. 对于6.828,我们将采用

MIT 6.828 JOS学习笔记2. Lab 1 Part 1.2: The kernel

Lab 1 Part 1: PC bootstrap 我们继续~ PC机的物理地址空间 这一节我们将深入的探究到底PC是如何启动的.首先我们看一下通常一个PC的物理地址空间是如何布局的:                           这张图仅仅展示了内存空间的一部分. 第一代PC处理器是16位字长的Intel 8088处理器,这类处理器只能访问1MB的地址空间,即0x00000000~0x000FFFFF.但是这1MB也不是用户都能利用到的,只有低640KB(0x00000000~0x00

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

Introduction 在这个实验中,我们将实现操作系统的一些基本功能,来实现用户环境下的进程的正常运行.你将会加强JOS内核的功能,为它增添一些重要的数据结构,用来记录用户进程环境的一些信息:创建一个单一的用户环境,并且加载一个程序运行它.你也可以让JOS内核能够完成用户环境所作出的任何系统调用,以及处理用户环境产生的各种异常. Part A: User Environments and Exception Handling 新包含的文件inc/env.h里面包含了JOS内核的有关用户环境(

MIT 6.828 JOS学习笔记10. Lab 1 Part 3: The kernel

Lab 1 Part 3: The kernel 现在我们将开始具体讨论一下JOS内核了.就像boot loader一样,内核开始的时候也是一些汇编语句,用于设置一些东西,来保证C语言的程序能够正确的执行. 使用虚拟内存 在运行boot loader时,boot loader中的链接地址(虚拟地址)和加载地址(物理地址)是一样的.但是当进入到内核程序后,这两种地址就不再相同了. 操作系统内核程序在虚拟地址空间通常会被链接到一个非常高的虚拟地址空间处,比如0xf0100000,目的就是能够让处理器

MIT 6.828 JOS学习笔记16. Lab 2.2

Part 3 Kernel Address Space JOS把32位线性地址虚拟空间划分成两个部分.其中用户环境(进程运行环境)通常占据低地址的那部分,叫用户地址空间.而操作系统内核总是占据高地址的部分,叫内核地址空间.这两个部分的分界线是定义在memlayout.h文件中的一个宏 ULIM.JOS为内核保留了接近256MB的虚拟地址空间.这就可以理解了,为什么在实验1中要给操作系统设计一个高地址的地址空间.如果不这样做,用户环境的地址空间就不够了. Permission and Fault

MIT 6.828 JOS学习笔记9. Exercise 1.5

Lab 1 Exercise 5 再一次追踪一下boot loader的一开始的几句指令,找到第一条满足如下条件的指令处: 当我修改了boot loader的链接地址,这个指令就会出现错误. 找到这样的指令后,把boot loader的链接地址修改一下,我们要在boot/Makefrag文件中修改它的链接地址,修改完成后运行  make clean, 然后通过make指令重新编译内核,再找到那条指令看看会发生什么. 最后别忘了改回来. 答: 这道题希望我们能够去修改boot loader的链接地

MIT 6.828 JOS学习笔记8. Exercise 1.4

Lab 1 Exercise 4 阅读关于C语言的指针部分的知识.最好的参考书自然是"The C Programming Language". 阅读5.1到5.5节.然后下载pointers.c的代码,并且编译运行它,确保你理解在屏幕上打印出来的所有的值是怎么来的.尤其要重点理解第1行,第6行的指针地址是如何得到的,以及在第2行到第4行的值是如何得到的,还有为什么在第5行打印出来的值看起来像程序崩溃了. 答: 首先编译运行文件pointer.c,得到如下结果: 首先程序声明了3个重要的

MIT 6.828 JOS学习笔记5. Exercise 1.3

Exercise 1.3 设置一个断点在地址0x7c00处,这是boot sector被加载的位置.然后让程序继续运行直到这个断点.跟踪/boot/boot.S文件的每一条指令,同时使用boot.S文件和系统为你反汇编出来的文件obj/boot/boot.asm.你也可以使用GDB的x/i指令来获取去任意一个机器指令的反汇编指令,把源文件boot.S文件和boot.asm文件以及在GDB反汇编出来的指令进行比较. 追踪到bootmain函数中,而且还要具体追踪到readsect()子函数里面.找

MIT 6.828 JOS学习笔记6. Appendix 1: 实模式(real mode)与保护模式(protected mode)

在我们阅读boot loader代码时,遇到了两个非常重要的概念,实模式(real mode)和保护模式(protected mode). 首先我们要知道这两种模式都是CPU的工作模式,实模式是早期CPU运行的工作模式,而保护模式则是现代CPU运行的模式. 但是为什么现代CPU在运行boot loader时仍旧要先进入实模式呢?就是为了实现软件的向后兼容性不得已才这样的. 下面我们分别看下这两种工作模式的基本原理. 实模式(real mode) 实模式出现于早期8088CPU时期.当时由于CPU