BIOS 时期
计算机启动,主板上电,可这个时候没有操作系统,内存也是空的,CPU 该怎么办呢?
在主板上,有一个东西叫 ROM(Read Only Memory,只读存储器),上面早就固化了一些初始化的程序,也就是 BIOS(Basic Input and Output System,基本输入输出系统)。如下图
此时处于实模式,20位的地址总线可访问1M的内存地址空间
1M 空间最上面的 0xF0000 到 0xFFFFF 这 64K 映射给 ROM
当电脑刚加电的时候,会做一些重置的工作,将 CS 设置为 0xFFFF,将 IP 设置为 0x0000,03中有讲起始地址 *16+ 偏移量” 凑够20位,所以第一条指令就会指向 0xFFFF0,正是在 ROM 的范围内,这里,有一个 JMP 命令会跳到 ROM 中做初始化工作的代码,BIOS 开始进行初始化的工作
BIOS的初始化工作有哪些呢?
1、检查一下系统的硬件是不是都好着
2、要建立一个中断向量表和中断服务程序,要用键盘和鼠标,它们都要通过中断进行的
3、在显示器上显示一些字符,在内存空间映射显存的空间
bootloader 时期
BIOS完成了初始化工作,但BIOS如何找到操作系统并加载运行起来呢?Linux 里面有一个工具,叫 Grub2,全称 Grand Unified Bootloader Version 2。
操作系统一般都安装在硬盘上,在BIOS中也会发现一个启动盘的选项。启动盘的特点:在第一个扇区,占 512 字节,而且以 0xAA55 结束。在 512 字节以内会启动相关的代码。
启动相关的那些代码是通过Grub2放进去的。
通过 grub2-mkconfig -o /boot/grub2/grub.cfg 来配置系统启动的选项,cat /boot/grub2/grub.cfg | more 查看grub.cfg配置信息,如下图
grub.cfg里面的选项会在系统启动的时候,成为一个列表。最终显示出来的结果如图
那么Grub2是如何将启动程序安装到相应的位置
使用 grub2-install /dev/sda
1、grub2 第一个要安装的就是 boot.img。
- boot.S 编译而成,一共 512 字节,正式安装到启动盘的第一个扇区。这个扇区通常称为MBR(Master Boot Record,主引导记录 / 扇区)。
- BIOS 完成任务后,会将 boot.img 从硬盘加载到内存中的 0x7c00 来运行
2、boot.img加载 grub2 的另一个镜像 core.img
core.img 由 lzma_decompress.img、diskboot.img、kernel.img 和一系列的模块组成,功能比较丰富,能做很多事情。
a、boot.img 先加载core.img 的第一个扇区diskboot.img,对应的代码是 diskboot.S,boot.img 将控制权交给 diskboot.img
b、diskboot.img 的任务就是将 core.img 的其他部分加载进来
- 先是解压缩程序 lzma_decompress.img,再往下是 kernel.img,最后是各个模块 module 对应的映像。kernel.img,不是 Linux 的内核,而是 grub 的内核。
- lzma_decompress.img 对应的代码是 startup_raw.S,用于解压压缩过的kernel.img
- lzma_decompress.img 还做了一个重要的决定,就是调用 real_to_prot,切换到保护模式,如果是32位系统地址总线变为32位,寻址空间由1M变为4G
从实模式切换到保护模式
切换到保护模式要干很多工作,大部分工作都与内存的访问方式有关。第一项是启用分段,就是在内存里面建立段描述符表,将寄存器里面的段寄存器变成段选择子,指向某个段描述符,这样就能实现不同进程的切换了。第二项是启动分页。
切换保护模式的函数 DATA32 call real_to_prot 会打开 Gate A20,也就是第 21 根地址线的控制线,寻址空间由1M变为4G。
对压缩过的 kernel.img 进行解压缩,然后跳转到 kernel.img 开始运行。
- kernel.img 对应的代码是 startup.S 以及一堆 c 文件,在 startup.S 中会调用 grub_main,这是 grub kernel 的主函数。
- grub_main这个函数里面,grub_load_config() 开始解析上面grub.conf 文件里的配置信息。
- grub_main 最后会调用 grub_command_execute (“normal”, 0, 0),最终会调用 grub_normal_execute() 函数,这个函数里面的grub_show_menu() 会显示出让你选择的哪个操作系统的列表。
- 从列表中启动某个操作系统,就要开始调用 grub_menu_execute_entry() ,开始解析并执行你选择的那一项。
里面的 linux16 命令,表示装载指定的内核文件,并传递内核启动参数。grub_cmd_linux() 函数会被调用,用于检查linux内核镜像头部的一些数据结构,如果通过则读取整个内核镜像到内存
最后会调用 grub_command_execute (“boot”, 0, 0) 才开始真正地启动内核。