开机后最开始的两行代码
开机后最开始的两行代码
此代码运行环境为x86架构的处理器
1 |
|
这两行代码是用汇编语言写的,含义是把 0x07c0 这个值复制到 ax 寄存器里,再将 ax 寄存器里的值复制到 ds 寄存器里。这一番折腾的结果就是让 ds 这个寄存器里的值变成了 0x07c0。
寄存器
上图是CPU中的关键寄存器,我们这两行代码用到ax是通用寄存器,通常存放一般性的数据,ds是段寄存器,存放数据的段地址。
由于CPU不支持直接将数据存入段寄存器,所以需要先将数据存入通用寄存器ax,再将ax寄存器里的值传给段寄存器ds
段寄存器的作用
x86架构最早是Intel的8086处理器,由于数据总线是16位的但地址总线是20位的,所以提出了段寄存器。当我们需要访问一个物理地址时,将16位的段地址和16位的偏移地址通过数据总线传给地址加法器,计算出实际的20位的物理地址。
地址加法器采用物理地址=段地址x16+偏移地址的方法用段地址和偏移地址合成物理地址
所以这两行代码就是将数据的段地址确定下来,以后对数据的操作都会和数据段地址计算物理地址
CPU怎么找到这两行代码
重装过系统的应该都听过BIOS,这个程序时被固化到主板中的。在按下启动键后,BIOS会先检查硬件是否正常,然后将硬盘中启动区的 512 字节的数据,原封不动复制到内存中的 0x7c00 位置处,并跳转到这里运行。
CPU如何执行BIOS程序
上电后CPU会发出reset信号,该信号会到0xffff0执行CPU的第一条指令,0xffff0处就是BIOS。0xffff0是Intel规定的地址。
启动区
Bios会到内存中检查第一扇区的结尾是不是0x55和0xaa,如果是,则这个扇区可以用于启动,就是一个启动区。
启动区一定在第一扇区,但第一扇区并不一定是启动区
Linux-0.11 最开始的代码就是这个用汇编语言写的 bootsect.s,编译以后会放在第一扇区,本章这两行代码就是 bootsect.s 中的前两条指令。
启动区的内容又叫主引导记录(Master boot record,缩写为MBR)。此时BIOS的任务就结束了,后续都交由MBR来处理,并由MBR来寻找操作系统。
为什么是0x07c0
0x7c00这个地址首次出现是在IBM PC Model 5150,它使用8088,8088处理器是8086处理器的阉割版本。
当时,搭配的操作系统是86-DOS。这个操作系统需要的内存最少是32KB。我们知道,内存地址从0x0000开始编号,32KB的内存就是`0x0000~0x7fff。
8088芯片本身需要占用0x0000~0x03ff
,用来保存各种中断处理程序的储存位置。所以,内存只剩下0x0400~0x7fff
可以使用。
操作系统启动后,主引导记录就没有用处了,主引导记录就被放到了内存地址的尾部,这样它所在的内存地址可以被操作系统重新利用(可以被新的数据覆盖)。
我们知道主引导记录(启动区)一共是512字节,它也需要保存可能产出的数据,要再预留512字节,所以它的位置就变成了:
1 |
|
0x7c00就是这样来的并一直延续了下来。
我们的数据是从这开始存的,所以数据段寄存器ds的值是0x07c0(计算地址时段寄存器要左移四位,就变成0x7c00 )。
0x55和0xaa
为什么是0x55和0xaa?目前有两种说法:
- 0x55对应ASCII码的U,也就是Unix的首字母。而0x55左移一位为0xAA,这里的A代指AT&T,因为Unix来自于“AT&T Unix”。意即,感谢AT&T诞生Unix
- AA展开为10101010,55展开为01010101,比较适用于校验。
当然我相信第二点,并认为第一点是巧合,是浪漫的误会。
总结
按下电源键,CPU进行了如下操作:
- 到0xffff0处找到BIOS,并运行
- BIOS完成自检等操作,将启动区512字节复制到0x7c00处,并执行启动区程序MBR
- MBR的前两行是设置数据段地址为0x07c0
本文是学习《Linux源码趣读》的笔记,同款书也有购买,是一本介绍Linux源码的好书。