一、Grub2的基本认识
(1)Grub2的起源
GRUB起源于1995年,当时Erich Boleyn想尝试使用Utah大学研发的Mach4微内核(现在被称为GNU Mach)引导GNU Hurd。由于当时互不兼容的个人计算机引导方案,Erich和Brain Ford一起设计出了多重引导规范。
Erich刚开始去修改FreeBSD的引导加载器让其能兼容多重启动,但他很快就意识到应当自己编写一个更容易且简单的引导加载程序,而不是继续坚持在FreeBSD的引导加载器上做改动,因此GRUB诞生了。Erich给GRUB添加了许多特性,但是它与生俱来的优势使得其能够迅速满足扩大的用户需求。1999年,Gorden Matzigkeit和Yoshinori K.Okuji采用GRUB作为正式的GNU软件包,并通过匿名CVS去开放它的源代码从而让项目去发展更新。接下来的几年里,GRUB与它的扩展也满足了许多要求,但很快GRUB就暴露出它固有的缺陷(本身并不能很好的兼容其扩展设计的问题),同时GRUB的维护者们也意识到很难在不破坏现有功能的基础上进行修补。于是大约在2002年,Yoshinori K.Okuji开始了PUPA(初步通用编程体系架构GNU GRUB)的工作,就是为了重写GRUB的核心以使其更简洁,更安全,更健壮,更强大。PUPA最终被命名为GRUB2,以前的GRUB版本被重新命名为Grub Legacy。少量的人员被继续留下来开发Grub Legacy,但是最后一个版本是2005年(0.97),也基本成为了最终版本。
在2007年左右,GNU/Linux发行版开始使用Grub2,2009年之后大多数发行版厂商默认安装Grub2。
(2)Grub2的优点
1)认识并支持更多的文件系统,并且可以通过Grub2的主程序直接在文件系统中搜寻kernel文件;
2)开机的时候,可以“自行编辑与修改开机设置项目”,即在线编辑功能;
3)可以动态搜寻配置文件,而不需要在修改配置文件后重新加载Grub2,即下次开机之后就会生效;
(3)Centos7中GRUB2初探
- 查看GRUB2的版本
[root@lnmp ~]$ rpm -qa |grep 'grub2' # 查看系统Grub2的版本
grub2-2.02-0.65.el7.centos.2.x86_64 - 查看GRUB2相关文件(/boot/grub2/*)
[root@lnmp /boot/grub2/i386-pc]$ ls -lhrt *.img # Grub由几个image组成
-rw-r--r--. 1 root root 27K Sep 7 2020 core.img
-rw-r--r--. 1 root root 512 Sep 7 2020 boot.img # boot.img大小固定为512Byte
[root@lnmp /boot/grub2/i386-pc]$ file boot.img # 查看img文件类型
boot.img: x86 boot sector; partition 4: ID=0xd4, starthead 205, startsector 4277266767, 0 sectors, code offset 0x63 # x86 boot sector 表明x86架构下的boot sector
(4)GRUB2的bootstrap image文件初探
Centos7系统/boot/grub2/i386-pc/*目录下存在许多image镜像文件,这些文件会被grub2-install命令安装到硬盘的相应位置。当BIOS执行完毕后,就会被加载并引导系统完成启动。
1. boot.img
boot.img是Grub2第一个被运行的程序,它被写入到MBR(Master Boot Record),固定大小为512B。boot.img功能很简单,主要是读取磁盘中core.img中的第一个扇区(sector)到内存中并执行相应的代码,因为仅有512B,boot.img不能够加载文件系统,并且只能从硬盘固定的位置加载。
2. diskboot.img
当从硬盘启动的时候这是core.img第一个扇区(sector)的内容,主要功能是读取剩下的core.img到内存中并开始运行kernel.img。同样diskboot.img没有文件系统的功能,当读取剩余的core.img时候,依然从硬盘固定位置读取。
3. kernel.img
GRUB2正确运行的基本组成:对设备及文件的处理框架,环境变量,恢复模式下的命令行等等。此模块很少被直接使用,一般是core.img中必不可少的一部分。
4. core.img
core.img是GRUB2的核心,包含了kernel.img及一些必要的modules,通常core.img包含了足够的模块(modules)为了访问xfs/ext4文件系统/boot/grub2目录,并且从/boot/grub2下加载所有剩余的模块,这些模块包含了启动目录处理,加载操作系统等等功能。目前disk限制core.img安装必须小于32KB,core.img一般被安装在硬盘特殊区域:embedding area。
5. *.mod
大部分模块会被core.img在运行时自动动态加载,其中小部分被整合到core.img中,比如文件系统xfs.mod等。
(5)GRUB2与GRUB Legacy之间的对比
很多人都熟悉 GRUB2 Legacy里面的stage1、stage1.5、stage2等概念,而在GRUB2里面,这些都被各种bootstrap image文件代替。
- stage1相当于boot.img完成的功能。
- stage1.5相当于core.img,但是core.img功能更加强大,在不加载其他模块的情况下,提供修复shell操作。
- state2相当于加载modules。
二、磁盘的基本认识
(1)磁盘的两种寻址方式
- CHS(cylinders-heads-sectors):CHS寻址模式将磁盘划分为磁头(Heads)、柱面(Cylinder)、扇区(Sector),其中MBR地址为0柱面、0磁头、1扇区。【基本不再使用】
- LBA(Logical Block Address):LBA是非常简单的一种寻址方式(从0开始编号来定位区块,依次类推),其中MBR地址为LBA0。
(2)boot.img和core.img安装
- 拷贝boot.img到LBA0的MBR引导代码中
- 拷贝core.img到LBA1-LBA31中空闲代码中
(3)硬盘的逻辑结构
三、Grub2启动过程
(1)boot.img
- Step1 无条件跳转 jmp short 0x7c65
- Step2 初始化
DL初始化为0x80
ax清零,ds赋值0,ss赋值0
sp初始化化为0x2000
- Step3 判断硬盘是否支持LBA还是只支持CHS
在加载后续GRUB2 core.img之前,系统必须判断硬盘是否支持LBA还是只支持CHS。备注目前普遍采用LBA模式,因此本文只讨论此模式。
- Step4 采用LBA加载core.img第一个扇区
调用BIOS INT14把硬盘LBA1的512字节传输到内存0x7000:[0x0000]位置
- Step5 拷贝core.img第一个扇区到内存的指定位置
把core.img第一个扇区从内存0x7000位置拷贝到0x8000位置
Q:为啥不直接将MBR加载到内存的0x80000位置?
A:在多个位置使用相同的代码或者将多个代码加载到同一个位置
1)多个位置重用相同的代码可以减少维护的成本负担
2)某些特定的机器或者媒介不仅仅只加载512个字节,比如CD代码总是加载2048字节的倍数,那么调用者可以只需复制它所需要的量,也可以选择使用偏移量
- Step6 执行core.img第一条语句
总结:
boot.img的主要功能就是将硬盘LBA1(0柱面,0磁道,2扇区)的512字节加载到内存0x80000处。这部分代码就是core.img的第一个扇区内容,一般都是GRUB2的diskboot.img。
(2)core.img
其中core.img包含diskboot.img,kernel.img以及*.mod模块
- diskboot.img
1)Step1 初始化
2)Step2 从硬盘读取core.img第二个扇区开始的其余若干扇区。该部分的数据依然拷贝至内存0x7000:[0x0000]处-【物理地址:0x70000】
3)Step3 拷贝缓冲区。将Step2部门的内存数据拷贝至0x0820:[0x0000]处-【物理地址:0x8200】
4)Step4 跳转至core.img其实地址0x8200开始执行
总结:disboot.img主要功能就是件LBA2-LBA67的硬盘内容拷贝至内存0x8200位置。
那么LBA2-LBA67这部分内容是什么?
1)开始部分是startup的代码
2)后一部分是压缩的core.img核心代码
至此,diskboot.img完成任务,由core.img中的startup接收
- GRUB2 core Startup包括两部分:Startup_raw和Startup
1)Startup_raw - 此段代码必须加载至0x8200
I Step1 初始化
II Step2 进入保护模式
III Step3 解压core.img
IV Step4 跳转核心代码
2)Startup - 此段代码必须加载到0x00100000
I Step1 初始化
II Step2 调用grub_main进入grub的主函数
- GRUB主函数main.c,即GRUB主要功能
I 各种模块的加载
II 读取 /boot/grub2/grub.cfg 显示启动菜单
III 根据以上菜单配置加载 linux kernel
menuentry 'My_CentOS Linux (3.10.0-862.el7.x86_64) 7 (Core)' --class centos --class gnu-linux --class gnu --class os --unrestricted $menuentry_id_option 'gnulinux-3.10.0-862.el7.x86_64-advanced-a9b65ac9-e434-4628-b5eb-0d547de76ce9' {
load_video
set gfxpayload=keep
insmod gzio
insmod part_msdos
insmod xfs
set root='hd0,msdos1'
if [ x$feature_platform_search_hint = xy ]; then
search --no-floppy --fs-uuid --set=root --hint-bios=hd0,msdos1 --hint-efi=hd0,msdos1 --hint-baremetal=ahci0,msdos1 --hint='hd0,msdos1' 6798c33b-1978-4b25-a465-189df158ff27
else
search --no-floppy --fs-uuid --set=root 6798c33b-1978-4b25-a465-189df158ff27
fi
linux16 /vmlinuz-3.10.0-862.el7.x86_64 root=/dev/mapper/centos_centos-root ro rd.lvm.lv=centos_centos/root # 加载内核 rd.lvm.lv=centos_centos/swap biosdevname=0 net.ifnames=0 rhgb quiet LANG=en_US.UTF-8
initrd16 /initramfs-3.10.0-862.el7.x86_64.img # 加载虚拟文件系统
}
备注:加载的这两个文件都在/boot目录下,可以用file命令查看文件类型。可以肯定,这个GRUB已经有了文件系统(Centos07的XFS)驱动,不会调用BIOS INT13去加载这两个文件,而是直接在XFS文件系统/boot目录下加载。
/* 实际上准备linux内核启动参数,在配置文件后面的参数都是可以传到内核中
* root:指出启动的根文件系统
* rhgb:redhat graphics boot就是会看到图片来代替启动过程中显示的文件信息
* quet:将kernel log level设置为KERN_WARNING
* lang:缺省语言
*/
1)Step1 linux16 加载内核与自定义配置参数
2)Step2 initrd16 加载虚拟文件系统
3)Step3 boot 加载
- kernel.img
Linux kernel的start()是当Kernel被grub2加载后,kernel的第一个入口方法,其地址为the kernel entry point is 9020:0000。
本文仅仅是对https://max.book118.com/html/2017/0918/134263842.shtm的整理,感谢原创!