Linix 启动过程详解

百科知识2025-04-261

启动linux kernel过程:

PC/AT机内存使用区域图: |-----------------------------------------------------| 0xffffffff(=4GB) |           jmp ROM_BIOS_LOCACTION |-----------------------------------------------------| 0xfffffff0(=4GB-16B) |                   ROM BIOS                          | |-----------------------------------------------------| 0xfffeffff(=4GB - 64KB) | |-----------------------------------------------------| 16MB | |-----------------------------------------------------| 0x100000(=1MB) |                 ROM BIOS Map                        | |-----------------------------------------------------| 0xf0000(=960KB) |                 Other BIOS Map                      | |-----------------------------------------------------| 0xe0000 |                Other Card ROM BIOS                  | |-----------------------------------------------------| 0xc7fff |                    VGA ROM BIOS                     | |-----------------------------------------------------| 0xc0000(=768KB) |                  Video buffer                       | |-----------------------------------------------------| 0xa0000(=640KB)(=640 * 1024B) | |-----------------------------------------------------| 0x00500 |                   BIOS  Data                        | |-----------------------------------------------------| 0x00400 |                Interrupt Vector                     | |-----------------------------------------------------| 0x00000

Note!!! 1) 为保持计算机在软件上的兼容,PC/AT在1MB以下物理内存使用分配上依然与8086兼容,因为 8086只有20根地址线,只能寻址1MB,只是原来系统ROM中BIOS一直处于CPU所能寻址的内存最 高端位置处,而BIOS原来所在位置(指BIOS在8086机器所能寻址的位置)被用作现在机器BIOS的 Shadow区域,即:把BIOS复制到此位置处. 2) 除了0xA0000~0xFFFFF(total=384KB): 用于I/O设备    0xfffe0000~0xffffffff(total=128KB): 用于BIOS 其余部分都可以作为系统内存

1.当计算机系统上电或者按下复位按钮时: CPU: cs = 0xF000,ip=0xFFF0, 故,段基地址=0xFFFF0000,段长度=64KB,CPU代码指针指向 0xFFFFFFF0,即:4GB空间最后一个64KB的最后一个16字节处,这里存放一条跳转指令,主机将 该跳转指令安排为BIOS的初始化程序的入口地址.

Note!!!(author: bogdan) Bochs在BIOS程序的第一条指令处中断 [0xfffffff0]f000:fff0 (unk. ctxt): jmp far f000:e05b ; ea5be000f0 查看CS寄存器的值 cs:s=0xf000, dl=0x0000ffff, dh=0xff0093ff, valid=1 查看ip的值: rip: 0x00000000:0000fff0 intel处理器,实模式下地址的计算为"段地址*16+偏移地址"即是f000*10+fff0=ffff0 即物理地址为[0x000ffff0];那么为什么会是[0xfffffff0]呢?

解释: 这时因为intel 286后,实模式下地址的计算根本就不是"段地址*16+偏移地址" 而是采用跟保护模式一样的地址计算方式,即采用描述符。也就是说,实模式下 段寄存器的缓冲区也是有用的,段寄存器的缓冲区是“不可见的”,它有8个字节。 cs:s=0xf000, dl=0x0000ffff, dh=0xff0093ff, valid=1,其中dl,dh就是它的缓冲寄存器。 段寄存器缓冲区的值就是描述符的值,我们知道,当寄存器的值不变时(valid=1,描述符有效, 这时,段寄存器的值无关紧要,是什么值应该都可以)是从段寄存器的缓冲区中取基地址的, 从dl=0x0000ffff, dh=0xff0093ff(描述符), 可以看出,段界限=0ffff,基地址=ffff0000, G=1(段界限粒度为字节,段大小限制为64k),d=0,avl=0,P=1(段已在内存中),DPL=0(ring0,特权级为0), s=1(存储段描述符),type=3(这里为什么不是大于等于8,我就不清楚了)所以物理地址是ffff0000+0000fff0=fffffff0, 即第一条指令的地址为[0xfffffff0]。接下来,实模式下,当段寄存器的地址发生变化的时候,就会把段寄存器的值左移4位, 放入段寄存器的缓冲器中的基地址部分,这应该是实模式转移指令做的事情。这样,在实模式下看起来,地址的计算方式就是"段地址*16+偏移地址"。 intel 为什么要这样做呢?完全是为了向上兼容处理器惹的祸!我们知道实模式下的地址都是物理地址,我们的内存根本就不能达到[0xfffffff0], 那么这个地址在哪里呢?其实,cpu启动的时候,这个地址应该是采取某种机制把它映射到rom中的(bios),在rom中执行到适当的时候, 会rombois拷贝到内存的1m后64k,并采用某种机制切换到内存中执行,以加快启动的速度。 基于这样的地址计算方式,其实在实模式下,完全可以访问到大于1M的空间, 这样做:合适地初始化各个描述符,做好必要的工作,暂时切换到保护模式,注意要开启A20地址线,然后再切换回实模式, 不要关闭a20,这样地址应该就不会回滚了。注意,进入保护模式不要开启分页,或者开启分页后保证线性地址是和物理地址一样的, 为了防止找不到你的指令,回保护模式的时候也不要改变段寄存器的值,因为改变段寄存器的值就改变段寄存器的缓冲寄存器的值了, 这样就访问不到大于1M的内存了,当然,访问不存在的地址可能会出现问题。

2.BIOS初始化过程包含两部分工作: 系统加电自检和系统自举(OS的装入和引导) 1)检测电脑系统中的内存,显卡等关键设备是否能够正常工作. 2)查找显卡的BIOS,然后调用显卡BIOS的初始化代码,用它完成显卡的初始化工作. 3)查找其他设备的BIOS程序,找到之后同样要调用这些BIOS的内部初始化代码来初始化设备。 4)显示BIOS自己的启动代码. 5)检测CPU的类型和工作频率,并将检测结果显示在屏幕上。 6)开始检测系统中安装的一些标准硬件设备(硬盘, CD-ROM等) 7)标准设备检测完成后开始检测一些即插即用设备

Note!!! 上述步骤是在打开电源开关或按Reset按钮进行冷启动时所要完成的各种初始化工作,如果 按Ctrl+Alt+Del键进行热启动时直接从第三步开始,同时会跳过第五步

8)BIOS在执行了一系列硬件检测和初始化工作之后,就会把与原来PC机兼容的64KB BIOS代码和 数据复制到低端1MB末端64KB处,然后跳转到这个地方并且让CPU进入真正的实模式模式工作. 9)加电自检完成后,按照指定的启动顺序从软盘,硬盘和光驱中寻找启动设备。 Note!! 若硬盘是启动设备:即硬盘的第一个扇区(0磁头,0磁道,1扇区),硬盘最开始的512B,其最后两个字节是0x55AA 这是引导扇区的标志,512个字节不能多一个字节也不能少一个字节. 10)找到启动设备后,将启动设备上的512个字节的OS引导程序加载到内存0x7c00处,并跳转到这个地方继续 执行引导操作.

3.到此开始执行Linux的引导程序boot Path: boot/bootsect.s. Func: 其被BIOS读入到内存物理地址0x7c00(31KB)处,当它被执行时就会吧自己移动到内存 物理地址0x90000(576KB)处,并把启动设备后2KB(= 4 sector)字节代码(boot/setup.s)读入到内存0x90200出,而 kernel的其他部分(system模块)则被读入到内存地址0x10000(64KB)开始处