jmpfarf000:e05b
意思是跳转到物理地址 0xfe05b 处开始执行(回忆下前面说的实模式下的地址计算方式)地址 0xfe05b 处开始,便是 BIOS 真正发挥作用的代码了,这块代码会检测一些外设信息,并初始化好硬件,建立中断向量表并填写中断例程这里的部分不要展开,这只是一段写死的程序而已,而且对理解开机启动过程无帮助,我们看后面精彩的部分,也就是 BIOS 的最后一项工作:加载启动区六、0x7c00 是啥该较真的地方就是要较真,我绝对不会让加载这种魔幻的词出现在这里,我们现在就来把它拆解成人话其实这个词也并不魔幻,加载在计算机领域就是指,把某设备上(比如硬盘)的程序复制到内存中的过程那加载启动区这个过程,翻译过来就是,BIOS 程序把启动区的内容复制到了内存中的某个区域好了,问题又自然出来了,启动区是哪里?被复制到了内存的哪个位置?然后呢?我们一个个来回答什么是启动区呢?即使你不知道,你也应该能够猜到,一定是符合某种特征的一块区域,于是人们把它就叫做启动区了,那要符合什么特征呢?先不急,不知道你有没有过设置 BIOS 启动顺序的经历,通常有 U 盘启动、硬盘启动、软盘启动、光盘启动等等,BIOS 会按照顺序,读取这些启动盘中位于 0 盘 0 道 1 扇区的内容至于磁盘格式的划分,本篇就不做讲解了,总之对于内存,我们给出一个数字地址就能获取到该地址的数据,而对于磁盘,我们需要给出磁头、柱面、扇区这三个信息才能定位某个位置的数据,都是描述位置的一种方式而已接着说, 这 0 盘 0 道 1 扇区的内容一共有 512 个字节,如果末尾的两个字节分别是 0x55 和 0xaa,那么 BIOS 就会认为它是个启动区如果不是,那么按顺序继续向下个设备中寻找位于 0 盘 0 道 1 扇区的内容如果最后发现都没找到符合条件的,那直接报出一个无启动区的错误BIOS 找到了这个启动区之后干嘛呢?哦,前面说过了是加载,就是把这 512 个字节的内容,一个比特都不少的全部复制到内存的 0x7c00 这个位置怎么复制的?当然是指令啦哪些指令呢?这里我只能简单说指令集中是有 in 和 out 的,用来将外设中的数据复制到内存,或者将内存中的数据复制到外设,用这两个指令,以及外设给我们提供的读取方式,就能做到这一点啦启动区内容此时已经被 BIOS 程序复制到了内存的 0x7c00 这个位置,然后呢?这个其实也不难猜测,启动区的内容就是我们自己写的代码了,复制到这里之后,就开始执行呗,之后我们的程序就接管了接下来的流程,BIOS 的使命也就结束啦所以复制完之后,接下来应该是一个跳转指令吧没错,正是这样,PC 寄存器的值变为 0x7c00,指令开始从这里执行咦?不知道你有没有发现,我们似乎不知不觉又把之前的一句魔法语言翻译成人话了,开头我们说:BIOS 把控制权转交给排在第一位的存储设备所以这句话是什么意思呢?就是 BIOS 把启动区的 512 字节复制到内存的 0x7c00 位置,并且用一条跳转指令将 pc 寄存器的值指向 0x7c00你看,这不是也没多几个字嘛,就把这个问题说得明明白白,简简单单哦,对了,现在似乎就剩下一个问题了,为什么非要是 0x7c00 呢?好问题,当然答案也很简单,那就是人家 BIOS 开发团队就是这样定的,之后也不好改了,不然不兼容为什么不好改?我们看一个简单的启动区 512 字节的代码(代码摘抄自《30 天自制操作系统》)
;hello-os;TAB=4ORG0x7c00;程序加载到内存的0x7c00这个位置;程序主体entry:MOVAX,0;初始化寄存器MOVSS,AXMOVSP,0x7c00MOVDS,AX;段寄存器初始化为0MOVES,AXMOVSI,msgputloop:MOVAL,[SI]ADDSI,1CMPAL,0;如果遇到0结尾的,就跳出循环不再打印新字符JEfinMOVAH,0x0e;指定文字MOVBX,15;指定颜色INT0x10;调用BIOS显示字符函数JMPputloopfin:HLTJMPfinmsg:DB0x0a,0x0a;换行、换行DB"hello-os"DB0x0a;换行DB0;0结尾RESB0x7dfe-$;填充0到512字节DB0x55,0xaa;可启动设备标识
我们看第一行:ORG0x7c00
这个数字就是刚刚说的启动区加载位置,这行汇编代码简单说就表示把下面的地址统统加上 0x7c00正因为 BIOS 将启动区的代码加载到了这里,因此有了一个偏移量,所以所有写启动区代码的人就需要在开头写死一个这样的代码,不然全都串位了然后正因为所有写操作系统的,启动区的第一行汇编代码都写死了这个数字,那 BIOS 开发者最初定的这个数字就不好改了,否则它得挨个联系各个操作系统的开发厂商,说唉我这个地址改一下哈,你们跟着改改在公司推动另一个团队改个代码都得大费周折,想想看这样的推动得耗费多大人力况且即使改了,之前的代码也都不兼容了,这不得被人们骂死啊再看最后一行:DB0x55,0xaa
这也验证了我们之前说的这 512 字节的最后两个字节得是 0x55 0xaa,BIOS 才会认为它是一个启动区,才会去加载它,仅此而已回过头来说 0x7c00 这个值,它其实就是一个规定死的值,但还是会有人问,那必然有它的合理性吧其实,我的解释也只能说是人家规定了这个值,后人们替他们解释这个合理性,并不是说当初人家就一定是这样想的,就好比我们做语文的阅读理解题一样第一个 BIOS 开发团队是 IBM PC 5150 BIOS,当时被认为的第一个操作系统是 DOS 1.0 操作系统,BIOS 团队就假设是为它服务的但操作系统还没出,BIOS 团队假设其操作系统需要的最小内存为 32 KBBIOS 希望自己所加载的启动区代码尽量靠后,这样比较“安全”,不至于过早的被其他程序覆盖掉可是如果仅仅留 512 字节又感觉太悬了,还有一些栈空间需要预留,那扩大到 1 KB 吧这样 32 KB 的末尾是 0x8000,减去 1KB(0x400) ,刚好等于 0x7c00哇塞,太精准了,这可以是一种解释方式七、启动区里的代码写了啥其实写到这,我这篇文章就应该戛然而止了,因为最初的那个问题已经解决了,CPU 已经开始马不停蹄地从我们预期的位置跑起来了,万事开头难,剩下的内容,就是操作系统想怎么玩就怎么玩了但我觉得还不够味,似乎还有些问题萦绕在你脑海里比如说这个问题:启动区里的代码写了啥?就 512 字节就是全部操作系统内容了?这是一个好问题,512 个字节确实干不了啥,现在的操作系统怎么也得按 M 为单位算吧,512 个字节远远不够呢,那是怎么回事呢?其实我们可以按照之前的思路猜测,BIOS 用很少的代码就把 512 字节的启动区内容加载到了内存,并跳转过去开始执行那按照这个套路,这 512 字节的启动区代码,是不是也可以把更多磁盘中存储的操作系统程序,加载到内存的某个位置,然后跳转过去呢?没错,就是这个套路所以 BIOS 负责加载了启动区,而启动区又负责加载真正的操作系统内核,这配合默契吧?由于用于启动盘的磁盘是人家写操作系统的厂商制作的,俗称制作启动盘,所以他也肯定知道操作系统的核心代码存储在磁盘的哪个扇区,因此启动区就把这个扇区,以及之后的好多好多扇区(具体取决于操作系统有多大)都读到内存中,然后跳转到开始的程序开始的位置跳转到哪里呢?这个就不像 0x7c00 这个数那么经典了,不同的操作系统肯定也不一样,也不用事先规定好,反正写操作系统的人给自己定一个就好了,别覆盖其他关键设备用到的区域就好八、操作系统内核写了啥好了现在经过好几轮跳跳跳,终于跳到内核代码啦,我们来一起回顾一下:按下开机键,CPU 将 PC 寄存器的值强制初始化为 0xffff0,这个位置是 BIOS 程序的入口地址(一跳)该入口地址处是一个跳转指令,跳转到 0xfe05b 位置,开始执行(二跳)执行了一些硬件检测工作后,最后一步将启动区内容加载到内存 0x7c00,并跳转到这里(三跳)启动区代码主要是加载操作系统内核,并跳转到加载处(四跳)经过这连续的四次跳跃,终于来到了操作系统的世界了,剩下的内容,可以说是整个操作系统课程所讲述的原理,分段、分页、建立中断、设备驱动、内存管理、进程管理、文件系统、用户态接口等等这些名词在操作系统的课程中你可能都或多或少听过,如果你好好学了的话也一定知道大概的原理,不过像笔者这样从头到尾研读过 linux 内核源码的硬核狗来说,这些概念不只是书本上枯燥无味的概念,而是活灵活现在操作系统的每一行代码上,有的展现了作者无比的智慧,有的让我看到了作者由于硬件设定不得已做出的屈服如果这篇文章提起了你对操作系统的好奇心,建议你也找时间读一读,和我一起入坑,你会发现一个新世界的大门向你打开了九、参考资料好了,这回我真的要结束了,相信如果你真的看完了全文,计算机的启动过程,可以说有了比较具象的了解如果你想深入细节,也就是了解整个过程的每一点,那可要下功夫了初学者推荐两本书籍,可以顺序阅读,祝你入坑:《30 天自制操作系统》《操作系统真象还原》(图片来源网络,侵删)
0 评论