一、Grub概念简介
GNU Grub(GRand Unified Bootloader简称“Grub”)是一个来自GNU项目的多操作系统启动程序,允许用户可以在计算机内同时拥有多个操作系统,并在计算机启动时选择希望运行的操作系统。引导加载程序是计算机启动时运行的第一个软件程序,它负责加载和传输控制操作系统内核软件,之后内核依次初始化操作系统的其余部分。
使用Grub启动时,可以使用命令行界面(参见命令行界面),也可以使用菜单界面(参见菜单界面)。在菜单中,可以切换到命令行模式,反之亦然。
使用命令行界面,可以手动输入内核的驱动器规范和文件名。
在菜单界面中,该菜单基于预先准备的配置文件(参见configuration),只需使用方向键选择操作系统。
图1 图形菜单界面
图2 命令行界面
- Grub使用方法
2.1 Grub正常启动
Grub正常启动后,将进入“普通模式”,一般是显示一个菜单(找到了'$prefix/Grub.cfg'),或者直接进入Grub SHELL(没找到'$prefix/Grub.cfg')。
在普通模式中,命令模块[command.lst]与加密模块[crypto.lst]会被自动按需载入(无需使用"insmod"命令),并且可使用完整的Grub脚本功能。但是其他模块则可能需要明确使用"insmod"命令来载入。
2.1.1 Grub在BIOS平台
Grub默认安装在第一个硬盘hd0的MBR上,把引导程序boot.img写入硬盘的MBR,启动时根据MBR提示找到启动分区,加载分区内的Grub核心文件core.img和配置文件Grub.cfg。(hd0,mdsom1)
BIOS --> boot.img[MBR] --> core.img --> 设置"prefix root cmdpath"环境变量 --> 加载"normal.mod"模块[同时还包括依赖的 terminal crypto extcmd boot gettext 模块] --> 执行"normal $prefix/Grub.cfg"命令。
2.1.2 Grub在UEFI平台
UEFI --> core.img[BOOTX64.EFI/BOOTX86.EFI] --> 设置"prefix root cmdpath"环境变量 --> 加载"normal.mod"模块[同时还包括它所依赖的 terminal crypto extcmd boot gettext 模块] --> 执行"normal $prefix/Grub.cfg"命令。
2.2 Grub版本查询
查询ubuntu的Grub版本号:
- Grub-install --version
- Grub-install -V
2.3 Grub菜单加上背景图
首先制作一张PNG格式的图片,分辨率最好是"1024x768"以保证较好的兼容性。然后将这张图片放到"$prefix/themes/1024x768.png"("$prefix"是Grub的安装目录)。然后在'Grub.cfg'中加入如下内容:
set gfxmode=1024x768,auto
insmod gfxterm
insmod png
terminal_output gfxterm
background_image $prefix/themes/1024x768.png
2.4 Grub显示中文界面(包括显示中文菜单项)
由于Grub在内部使用UTF-8编码,并且所有文本文件(包括'Grub.cfg')也都被假定为使用UTF-8编码,为了避免乱码,请务必以UTF-8编码保存'Grub.cfg'文件。
set gfxterm_font=unicode
set lang=zh_CN
set locale_dir=$prefix/locale
insmod gfxterm
terminal_output gfxterm
loadfont unicode
2.5 更改Grub的字体
如果默认的unicode字体在1024x768或更高分辨率的屏幕上显得太小,或者默认的字体不好看,可以使用"Grub-mkfont"工具。下面的示例展示了制作一个24px大小的pf2字体:
Grub-mkfont -i1 -n WenQuanYiMicroHeiMono24px -o WenQuanYiMicroHeiMono24px.pf2 -s24 -v wqy-microhei.ttc
将制作好的字体文件(WenQuanYiMicroHeiMono24px.pf2)放到"$prefix/fonts"目录中,修改'Grub.cfg'文件中的两行:
set gfxterm_font=WenQuanYiMicroHeiMono24px
loadfont WenQuanYiMicroHeiMono24px
2.6 Grub-2.0.6版本官方模块
2.6.1 命令模块[command.lst]
提供了各种不同的功能,类似标准Unix命令。
insmod module:载入名为"module"的Grub模块。
set [envvar=value]:将环境变量"envvar"的值设为'value'。如果没有使用参数,则打印出所有环境变量及其值。
help [pattern …]:显示内建命令的帮助信息。如果没有指定"pattern",那么将显示所有可用命令的简短描述。如果指定了"pattern",那么将只显示名字以这些"pattern"开头的命令的详细帮助信息。
halt [--no-apm]:关闭计算机。如果指定了 --no-apm 选项,表示不执行APM BIOS调用。否则,计算机使用APM关闭。
boot:启动已经被载入的OS或链式加载器。仅在运行于交互式命令行的时候才是需要的。在一个菜单项结束时是隐含的。
cat [--dos] file:显示文件"file"的内容。如果使用了"--dos"选项,那么"回车/换行符"将被显示为一个简单的换行符。否则,回车符将被显示为一个控制符(<d>)。
ls [arg …]:如果不使用参数,那么列出所有对Grub已知的设备。
如果参数是包含在括号内的一个设备名,那么列出该设备根目录下的所有文件。
如果参数是以绝对路径给出的目录,那么列出这个目录的内容。
lsmod:列出已经加载的所有模块
normal_exit:退出当前的普通模式。如果这个普通模式实例不是嵌套在另一个普通模式里的话,就会返回到救援模式。
2.6.2 加密模块[crypto.lst]
提供了各种数据完整性校验与密码算法支持,一共20多个。例如:gcry_rijndael crc64 gcry_md5 ...
2.6.3 文件系统模块[fs.lst]
提供了访问各种文件系统的功能,一共30多个。例如:btrfs cpio exfat ext2 fat iso9660 ntfs tar xfs zfs ...
2.6.4 分区模块[partmap.lst]
提供了识别各种分区格式的功能,一共10多个。例如:part_bsd part_gpt part_msdos ...
2.6.5 分区工具[parttool.lst]
提供了操作各种分区格式的功能,目前只有 msdospart 这一个。
2.6.6 终端模块[terminal.lst]
提供了各种不同终端的支持,一共不到10个。例如:serial gfxterm vga_text at_keyboard ...
2.6.7 视频模块[video.lst]
提供了各种不同的视频模式支持,一共6个。例如:vga vbe efi_gop efi_uga ...
2.6.8 其他模块
所有未在上述分类文件中列出的模块都归为这一类,一共将近100个。值得关注的有以下几个:
"all_video"可用于一次性加载当前所有可用的视频模块;
"gfxmenu"可用于提供主题支持;
"jpeg png tga"可用于提供特定格式的背景图片支持;
"xzio gzio lzopio"可用于提供特定压缩格式支持(常配合"initrd"命令使用);
2.7 Grub救援模式Rescue
进入救援模式,这意味着Grub由于某种原因未能加载“模块”或者Grub没有在正确的位置读取。
# Inspect the current prefix (and other preset variables):
set 可以输入set查看一下当前信息
# Find out which devices are available:
ls 查看Grub位置
# Set to the correct value, which might be something like this:
set prefix=(hd0)/Grub
set root=(hd0)
insmod normal
normal 回到引导界面上
- Grub配置文件
3.1 Grub配置文件生成与更新
Grub-mkconfig脚本自动生成 /boot/Grub/Grub.cfg,不需要手动修改。
# Grub-mkconfig -o /boot/Grub/Grub.cfg
/etc/Grub.d/脚本包含 Grub 菜单信息和操作系统引导脚本,运行 update-grub 命令时,它会读取 Grub 文件和 Grub.d 脚本的内容,并更新创建Grub.cfg文件。
3.2 Grub配置文件详解
下图为在Linux系统下,/etc/default/Grub内包含的自定义Grub配置文件。
(1)Grub_DEFAULT
默认的菜单项,默认值为0。其值可为数值N,表示从0开始计算的第N项是默认菜单
也可以指定对应的title表示该项为默认的菜单项。使用数值比较好,因为使用的title可能包含了容易改变的设备名
(2)Grub_SAVEDEFAULT
默认该key的值未设置。如果该key的值设置为true时,如果选定了某菜单项,则该菜单项将被认为是新的默认菜单项。该key只有在设置了"Grub_DEFAULT=saved"时才有效。
(3)Grub_TIMEOUT
在开机选择菜单项的超时时间,超过该时间将使用默认的菜单项来引导对应的操作系统。
默认值为5秒。等待过程中,按下任意按键都可以中断等待。
设置为0时,将不列出菜单直接使用默认的菜单项引导与之对应的操作系统,设置为"-1"时将永久等待选择。
(4)Grub_TIMEOUT_STYLE
如果该key未设置值或者设置的值为"menu",则列出启动菜单项,并等待"Grub_TIMEOUT"指定的超时时间。
如果设置为"countdown"和"hidden",则不显示启动菜单项,而是直接等待"Grub_TIMEOUT"指定的超时时间。如果超时了则启动默认菜单项并引导对应的操作系统
(5)Grub_BACKGROUND
设置背景图片,背景图片必须是Grub可读的,图片文件名后缀必须是".png"、".tga"、".jpg"、".jpeg",在需要的时候,Grub会按比例缩小图片的大小以适配屏幕大小。
(6)Grub_THEME
设置Grub菜单的主题。
四、Grub源码分析
4.1 Grub源代码下载
Grub源代码下载地址:https://ftp.gnu.org/gnu/Grub/,如下图所示根据系统编译环境选择需要的Grub源代码。
4.2 Grub源代码文件简介
本次选择下载Grub-2.06.tar.gz
在终端输入 tar zxvf Grub-2.06.tar.gz解压文件,进入Grub-2.06文件夹
asm-tests:这里面是几个汇编代码文件;
build-aux:包含了编译时可能用到的脚本;
conf:编译需要用的文件,make的时候会用到;
docs:帮助文件,还有Grub.cfg示例模板;
Grub-core:Grub的主体核心代码;
include:Grub主体的头文件;
m4:包含m4文件,configure的时候会使用到;
po:这个目录下主要由两种文件,一种时.po文件,另一种时.gmo文件。它们是用来支持多语言的,这个文件夹内的东西不要管,只是对代码做一些基本的注释;
tests:一些测试的脚本;
themes:Grub界面的主题;
unicode:Unicode数据;
util:一些工具源的文件。
启动时运行的代码在' Grub-core '的子目录中,在启动完整操作系统后运行的代码在顶层的子目录中。
4.3 Grub核心代码详解
Grub内核在' Grub -core/kern/ '中,它包含一些核心工具,比如设备、磁盘和文件框架、环境变量处理、列表处理等等。
Grub-2.06/Grub-core/kern/main.c 调用其它外部函数(没有在 main.c 中定义的);
Grub-2.06/Grub-core/kern/ 目录下的其它源文件,分别提供这些外部函数的定义(实现了这些函数的具体功能);
Grub-2.06/include/Grub/ 目录下的头文件,则对应这些外部函数的声明;
Grub-2.06/Grub-core/kern/ 目录下的与这些头文件与同名的 C 源文件;定义main.c 包含的这 12 个头文件,全部位于Grub-2.06/include/Grub/ 目录下。
终端实现在' Grub-core/term/ '中。
磁盘访问代码分布在' Grub-core/ Disk / '(用于访问磁盘设备本身),' Grub-core/partmap/ '(用于解释分区表数据),和' Grub-core/fs/ '(用于访问文件系统)。注意,除了一些特殊的例外,Grub只包含从文件系统读取的代码,并试图避免包含任何要写入文件系统的代码;这让我们可以放心地向用户保证,Grub不会导致文件系统损坏。
PCI和USB总线处理在' Grub-core/bus/ '中。
视频处理代码在' Grub-core/ Video / '中,图形化菜单系统大量使用这个功能。
大多数命令都是通过' Grub-core/commands/ '中的文件实现的。
在Grub_main之前,有一段汇编代码:
start:
_start:
movq %rcx, EXT_C(Grub_efi_image_handle)(%rip)
movq %rdx, EXT_C(Grub_efi_system_table)(%rip)
andq $~0xf, %rsp
call EXT_C(Grub_main)
进入Grub源代码,执行Grub_main函数。
- Grub编译与安装
5.1 卸载Grub
卸载掉旧的Grub:apt-get purge Grub-pc。
5.2 UEFI模式下编译生成Grub
Grub使用Autoconf和Automake,大部分Automake输入由Python脚本生成。顶级构建规则在' configuration .ac '中,‘Grub-core / Makefile.core.def’,‘Makefile.util.def’。' *.def '文件中的每个块代表一个构建目标,并指定用于在不同平台上构建该目标的源文件。' *.def '文件被' gentpl.py '处理成Automake输入。
使用configure工具生成Makefile文件时:
./configure --prefix=/usr(安装目录) --with-platform=efi(指定平台pc/uefi) --target=x86_64(指定目标的处理器架构类型,或i386) --sysconfdir=/etc(配置文件目录) --disable-werror(不把报警归结于错误)
make -j4让make最多允许4个编译命令同时执行,这样可以更有效的利用CPU资源
make install 安装编译生成的文件
Grub-install /dev/sda 安装到启动盘
sudo update-Grub 更新grub配置文件
5.3 Grub中Makefile的指令
5.3.1 clean
清除当前目录下在 make 过程中产生的文件。它不能删除软件包的配置文件,也不能删除 build 时创建的那些文件。
5.3.2 distclean
类似于"clean",但增加删除当前目录下的的配置文件、build 过程产生的文件。
5.3.3 info
产生必要的 Info 文档。
5.3.4 check 或 test
完成所有的自检功能。在执行检查之前,应确保所有程序已经被创建(但可以尚未安装)。为了进行测试,需要实现在程序没有安装的情况下被执行的测试命令。
5.3.5 install
完成程序的编译并将最终的可执行程序、库文件等拷贝到指定的目录。此种安装一般不对可执行程序进行 strip 操作。
5.3.6 install-strip
和"install"类似,但是会对复制到安装目录下的可执行文件进行 strip 操作。
5.3.7 uninstall
删除所有由"install"安装的文件。
5.3.8 installcheck
执行安装检查。在执行安装检查之前,需要确保所有程序已经被创建并且被安装。
5.3.9 installdirs
创建安装目录及其子目录。它不能更改软件的编译目录,而仅仅是创建程序的安装目录。
5.4 Grub编译
Grub源代码不需要修改makefile文件,添加新的.c和.h文件后,需要修改配置文件。如果添加一个新的模块遵循现有的模式,比如一个新的命令或一个新的文件系统,只需要修改两个文件内容。
此处修改Hello模块命令代码,新增对硬盘文件读取与SM3度量,文件内容为abc。
5.4.1 Grub-core/Makefile.core.am
Platfor_PROGRAMS
*************
*.marker:**
5.4.2 Grub-core/Makefile.core.def
添加新的模块
Module = {
Name = cmd;
Common = cmd/cmd.c;
};
5.4.3 hello模块
Grub源代码在发布时候,给出了一个测试指令模块,hello.mod。如果在Grub界面,按c进入命令模式,输入hello,会得到下图:
添加类似hello这类命令时,按照上述方式,在Grub-core/目录下添加ptest文件夹,加入ptest.c文件,修改Makefile文件即可添加命令模块。
- Grub文件介绍
6.1 /Boot目录
vmlinuz是可引导的、压缩的内核。“vm”代表“Virtual Memory”。Linux支持虚拟内存,
zImage(vmlinuz)和bzImage(vmlinuz)都是用gzip压缩的。它们不仅是一个压缩文件,而且在这两个文件的开头部分内嵌有 gzip解压缩代码。
vmlinux是未压缩的内核,vmlinuz是vmlinux的压缩文件。
initrd是文件系统,“initial ramdisk”的简写,initrd一般被用来临时地引导硬件到实际内核vmlinuz能够接管并继续引导的状态。
System.map是内核符号映射表,顾名思义就是将内核中的符号(也就是内核中的函数)和它的地址能联系起来的一个列表。
6.2 /Boot/Grub目录
[root@study ~]# ls -l /boot/Grub
-rw-r--r--. device.map <==Grub 的设备对应文件(下面会谈到)
drwxr-xr-x. fonts <==启动过程中的画面会使用到的字体数据
-rw-r--r--. Grub.cfg <==Grub 的主配置文件!相当重要!
-rw-r--r--. Grubenv <==一些环境区块的符号
drwxr-xr-x. i386-pc <==针对一般 x86 PC所需要的 Grub 的相关模块
drwxr-xr-x. locale <==就是语言相关的数据啰
drwxr-xr-x. themes <==一些启动主题画面数据
[root@study ~]# ls -l /boot/Grub/i386-pc
-rw-r--r--. acpi.mod <==电源管理有关的模块
-rw-r--r--. ata.mod <==磁盘有关的模块
-rw-r--r--. chain.mod <==进行 loader 控制权移交的相关模块
-rw-r--r--. command.lst <==一些指令相关性的列表
-rw-r--r--. efiemu32.o <==下面几个则是与 uefi BIOS 相关的模块
-rw-r--r--. efiemu64.o
-rw-r--r--. efiemu.mod
-rw-r--r--. ext2.mod <==EXT 文件系统家族相关模块
-rw-r--r--. fat.mod <==FAT 文件系统模块
-rw-r--r--. gcry_sha256.mod <==常见的加密模块
-rw-r--r--. gcry_sha512.mod
-rw-r--r--. iso9660.mod <==光盘文件系统模块
-rw-r--r--. lvm.mod <==LVM 文件系统模块
-rw-r--r--. mdraid09.mod <==软件磁盘阵列模块
-rw-r--r--. minix.mod <==MINIX 相关文件系统模块
-rw-r--r--. msdospart.mod <==一般 MBR 分区表
-rw-r--r--. part_gpt.mod <==GPT 分区表
-rw-r--r--. part_msdos.mod <==MBR 分区表
-rw-r--r--. scsi.mod <==SCSI 相关模块
-rw-r--r--. usb_keyboard.mod <==下面两个为 USB 相关模块
-rw-r--r--. usb.mod
-rw-r--r--. vga.mod <==VGA 显示适配器相关模块
-rw-r--r--. xfs.mod <==XFS 文件系统模块
-rw-r--r--. hello.mod <==hello模块,用来做基本的测试