##计算机的启动过程 ###如何将编译好的程序直接交给处理器执行
我们这一系列文章是学习X86汇编-从实模式到保护模式。目前我们还没有写过汇编程序。如果我们写好了一个汇编程序,使用特定的汇编编译软件来将汇编程序编译好,然后再使用特定的平台来运行这个程序即可。但是我们的目的不是这么简单。
我们的目的是学习操作系统与计算机系统之间的关系。那么我们如何执行我们写好的汇编代码呢?
每种编译器都有能力针对不同的操作系统将你的源程序编译为不同的(支持该操作系统平台的)二进制文件。然后运行这个二进制文件。然后不同的操作系统开发的软件也不一样,写的程序也不一样(在这里我们特指汇编语言),这样开发起来就很麻烦。
换个角度想一下,操作系统也是一个软件,它也是需要处理器对它取指令和执行指令的软件。只不过它比一般的软件更加复杂而已。如果我们能够绕过操作系统,让我们写的程序直接被处理器执行,而不需要操作系统的干预的话,或者让电脑一开机就执行我们自己写的程序,而不是加载你的操作系统的话,那么就可以实现让CPU直接过来执行我们的代码,这样看来更简单!!!
这个主意完全可行!!!但是我们还需要了解一些知识。比如计算机是如何启动后加载操作系统的。
###计算机的加电复位
在众多处理器的引脚中有一个RESET引脚,用于接收复位信号。每当处理器加电或者复位,处理器都会执行一个硬件初始化,以及一个内部自测程序(BIST),然后将内部所有的寄存器的值初始化到一个预置的状态。
比如,针对8086处理器来说,复位将使代码段寄存器(CS)的内容初始化到0xFFFF,其他所有寄存器初始化为0x0000.
处理器天生的职责就是取指令与执行指令。当你加电或者复位后,处理器就兴奋了,马上就想去取指令。但是有一个问题,现在内存中,可是什么都没有呢。无法取指令啊。至于内存中为什么没有东西,这个大家应该很了解了,因为内存是掉电丢失的,关闭电源后内存中所有的运行的程序包括操作系统程序都会丢失。
那么既然内存什么都没有,处理器如何取指令和执行指令。
BIOS就此登场!!!
BIOS-基本输入输出系统
注意,我们现在还是停留在8086,如果有一些知识与你所知道的不一样,那肯定是因为你所知道的不是8086的,有可能是以后出现的IA-32等。
8086,具有20根地址线(可以阅读文章查看为什么是20根地址线:处理器、内存、指令),它的寻址范围是0x00000-0xFFFFF,可以寻址1M的内存空间。我们之前学过,我们的内存条(物理内存)就是映射到这1M的寻址空间,CPU才能在这1M的寻址空间中对内存进行寻址。
实际上,这20根地址线并没有全部用于内存寻址,只不过是大部分地址线用于内存,还有一部分地址线用于只读存储器ROM和外围设备。
上图中上面的是ROM,下面的是内存条。外设的话放到以后讲解。
ROM是什么?ROM是只读存储器,它不管是在有电的是时候还是没电的时候,它存储的内容不会消失。它存储的内容是预先写好的,一般无法改变。这个特点很有用,比如,可以在上面预先写好一些程序指令。当处理器上电后,然后处理器先去这个ROM中取指令执行。这样就不会让处理器上电后在那干等着。
在8086中,ROM占据着处理器1M寻址空间的最顶端的64KB。物理地址的范围是0xF0000-0xFFFFF。这一段空间固化了开机时要执行的指令。
物理内存占据着处理器1M寻址空间的较低端的640KB。地址范围是0x00000-0x9FFFF。中间还有一部分地址空间是用于外围设备的。
处理器加电或者复位后,CS寄存器被赋予初值0xFFFF,IP寄存器赋予初值0x0000。所以此时需要访问的物理地址为0xFFFF0。它正好位于ROM中。
处理器取指令执行的自然顺序是从内存的低地址往高低地址推进。如果从 0xFFFF0 开始执行,这个位置离 1MB 内存的顶端(物理地址 0xFFFFF)只有 16 个字节的长度,一旦 IP 寄存器的值超过 0x000F,比如 IP=0x0011,那么,它与 CS 一起形成的物理地址将因为溢出而变成 0x00001,这将回绕到 1MB 内存的最低端。
所以在这个ROM中的这个位置,是一个跳转指令jmp。jmp指令通过修改CS与IP,使处理器从ROM的另一个位置开始取指令。在 NASM 汇编语言里,一个典型的跳转指令像这样:
jmp 0xf000:0xe05b
好了,此时处理器开始稳了,不再到处跳了,安安静静的从ROM的这个位置取指令执行指令。。。。。
那么这个ROM中存的是什么指令呢?为什么让CPU上来先执行这里的代码?
这个ROM中,主要进行硬件的检测,诊断和初始化。这里的检测和诊断很好理解。初始化的话,是让电脑出于一种可以显示屏幕,可以使用键盘等这样的基本功能。比如你重装系统的时候,会有界面供你选择如何一步步安装操作系统,甚至可以用鼠标键盘操作。这种基本的状态,就是由ROM中的代码初始化而来的。
正因为这种基本的状态,这个ROM芯片又叫做:BIOS(基本输入输出系统)ROM。我们这里不讲BIOS。
ROM-BIOS 的容量很小,它执行完后,就会让处理器去其他地方接着取指令执行。这个其他地方,就是主引导扇区,实际上这个主引导扇区,就是在硬盘的某一个位置。
那么现在问题就来了,在哪一个位置?处理器如何对它进行读写操作?
想要知道这个,就得先了解硬盘及其工作原理。
硬盘及其工作原理
如下图是一个拆开的硬盘和硬盘的构造:
硬盘可以有多个盘片或者一个盘片。他们串在一个轴上,由电机带着高速旋转。
每个盘片都有两个磁头,上面一个,下面一个。所以经常用磁头来指代盘面。磁头有编号,第一个盘片上,上面的磁头编号是0,下面的磁头编号是1;第二个盘片,上面的磁头编号是2,下面的磁头编号是3,以此类推。
磁头是由磁头臂固定在同一个支架上,由步进电机带着在盘片的中心和边缘移动。
可以想象,当盘片高速旋转时,磁头每步进一次,就会在盘面上形成一个圆,这个看不见的圆被称为磁道。磁道,是数据记录的轨迹。从上往下所有的磁道形成一个圆柱,称为柱面。
磁道,或者柱面也要编号。编号是从最边缘的磁道开始编号,最边缘的是0号磁道,向里依次递增。
柱面是一个用来优化数据读写的概念。在硬盘上,数据的访问是以柱面来组织的。
实际上,磁道还可以进一步划分成扇区:磁道很窄,也看不见,但在想象中,它仍呈带状,占有一定的宽度。将它划分许多段后,每一部分都呈扇形,这就是扇区的由来。其实扇区,才是最小的读写单位。
每个磁道能划分成几个扇区,取决于磁盘制造者,但通常为63个。每一个扇区也有一个编号,扇区的编号是从1号开始。
扇区与扇区之间是以间隙隔开。每个扇区以扇区头开始,然后是512字节的数据区。扇区头包含了每个扇区自己的信息,包括这个扇区所在的磁道号,磁头号,和扇区号。
一切从主引导扇区开始
讲了这么多硬盘的构造,实际上就是为了引出主引导扇区的概念。
主引导扇区:硬盘的第一个扇区是0面0道1扇区,或者叫做0头0柱1扇区。这就是主引导扇区。也就是处理器执行完BIOS-ROM中的指令后,需要执行指令的地方。当然,是BIOS-ROM将主引导扇区的第一条指令加载到内存中,也就是内存地址:0x07C00(0x0000:0x7c00)。 实际上就是一条jmp指令:
jmp 0x0000:0x7c00
至于为什么要从0x07c00开始,我查了是历史原因导致的! 出处bloghttp://blog.sina.com.cn/s/blog_13f5114cf0102wrqo.html 因为大部分是英文,我简单看了一下就大概说一下: 1981年8月,IBM公司最早的个人电脑IBM PC 5150上市,就用了8088这个芯片。 当时,搭配的操作系统是86-DOS。这个操作系统需要的内存最少是32KB。我们知道,内存地址从0x0000开始编号,32KB的内存就是0x0000~0x7FFF。 8088芯片本身需要占用0x0000~0x03FF,用来保存各种中断处理程序的储存位置。(主引导记录本身就是中断信号INT 19h的处理程序。)所以,内存只剩下0x0400~0x7FFF可以使用。 为了把尽量多的连续内存留给操作系统,主引导记录就被放到了内存地址的尾部。由于一个扇区是512字节,主引导记录本身也会产生数据,需要另外留出512字节保存。所以,它的预留位置就变成了: 0x7FFF – 512 – 512 = 0x7C00 0x7C00就是这样来的。 计算机启动后,32KB内存的使用情况如下。
+——————— 0x0
| Interrupts vectors
+——————— 0x400
| BIOS data area
+——————— 0x5??
| OS load area
+——————— 0x7C00
| Boot sector
+——————— 0x7E00
| Boot data/stack
+——————— 0x7FFF
| (not used)
+——————— (…)
主引导扇区的功能是,继续从硬盘的其他地方读取更多的指令加以执行,比如最后肯定会加载操作系统!!!
现在终于可以回到最开始的问题。我们如果把我们写好的自己的汇编代码,放到主引导扇区的位置上,是不是就可以让处理器直接执行我们的代码了!!!
肯定是的!!!
但是有一个问题就是,我们不能在我们自己电脑上做实验,因为你将你的代码写到主引导扇区,那么以后你的电脑就瘫痪了,无法启动操作系统了!!!
不过,虚拟机是一个好家伙。我们可以在虚拟机上虚拟一个计算机出来,在上面做实验。下篇文章讲解虚拟机!!!
我个人比较喜欢使用virtual box 虚拟机!