您的位置:首页 > 其它

grub2配置原理分析

2017-10-14 19:30 1606 查看

综述

BootLoader(引导程序)是系统启动之后第一个运行的程序。它的主要作用是加载操作系统并转入操作系统的入口,接下来的系统将由操作系统接管。随着技术发展,计算机系统的部署越来越复杂。从硬件角度,计算机引入了许多新的技术用以提供系统的性能,如SAS接口磁盘、NVMe接口磁盘、APIC等等,从软件角度,系统可能会安装在不同的文件系统上,甚至需要支持RAID、逻辑卷等底层的数据组织架构。复杂的环境对BootLoad有的设计带来了很大的挑战。BootLoad需要设计能够应对各种硬件的驱动程序,而且需要能够识别越来越复杂的数据存储结构。引导程序逐渐成为了一个在操作系统之前的操作系统。

Grub是一个可以被广泛应用的引导程序。他即能支持多种多样的硬件平台,也能够与包括Linux、windows在内的多种操作系统进行对接,它甚至可以能够引导安装在软RAID上的操作系统1。当采用Grub进行引导时,你即可以用他的图形界面选择启动项,也可以通过它提供的命令行在启动之前对启动项的各项进行编辑。而且,对于我们经常进行内核开发的场景来说,Grub提供的命令操作是非常有用的。

2002年始,Grub的维护者 Yoshinori K. Okuji开始重写Grub的相关程序,在架构上对其进行了重构,引入了模块化、基于Shell的配置文件生成机制、类脚本的编程的配置文件格式等新的设计思想,彻底的解决了Grub原有设计上的一些限制。业界将重构版的Grub称做Grub2,以区分原有的Grub。自2007年始,Grub2逐渐被各大发行版所接受,代替原有的引导程序。

与原有的grub相比,有很多新的不同点。在其用户手册中,主要总结了如下几点:

* 采用grub.cfg作为配置文件名字。采用新的配置文件语法

* grub.cfg可以通过grub-mkconfig进行自动生成。围绕grub-mkconfig,Grub2提供许多自动生成脚本,包括linux启动项自动搜索、适于Xen的配置项及内存检测配置项等。你可以在
/etc/grub.d
中找到他们。

* 分区编号开始从1开始

* 配置文件支持脚本化编写,支持变量、条件、循环

* 提供环境块机制用以保存少量的启动过程信息(操作系统加载之前的)

* 能够支持多盘引导,可以通过磁盘标签、磁盘的UUID确定磁盘

* 支持更多的系统,包括PC BIOS、PC EFI等等

* 提供图形化的终端

* 支持更多的文件系统

* 支持LVM、RAID等机制

* 重新组织引导镜像,不再使用Stage 1, Stage 1.5, and Stage 2作为引导阶段

* 支持动态模块加载,可以根据需要加载不同的驱动

如果有兴趣或者必要,可以对上述的区别进行详累的分析。

假设我们编译一个新内核,以期在现有的系统进行引导,需要做的动作如下:

将对应的内核镜像和initramf复制到
/boot
目录

复制内核模块到
/usr/lib/modules
目录

调用grub-mkconfig这个命令重新生成一下配置文件。

前两步由内核的编译程序可以帮你完成。后面一步是grub提供的程序完成的。这一步会重新检索
/boot
目录,将识别到可引导的操作系统引导项加入到grub.cfg中。

第三步我们可以手动操作。从配置文件中复制一项以munuentry开头的命令项,将内核及initcramfs替换成新的项就可以。之所以可以这做手动操作,是因为,Grub启动引导时只与grub.cfg这一个配置文件有关。与
/etc/grub.d
中的所有配置都是无关的。

grub.cfg基本语法

前面已经提到,grub.cfg是脚本化的,语法类似于Shell的语法。但是要注意,它不是一个Shell脚本,我们不能使用在本Shell所能使用的指令去编写这个脚本(比如,做一些字符处理什么的)。脚本里面的命令均是Grub的内置命令,其执行机理与操作系统Shell无一点关系。在这个脚本执行时,我们的操作系统内核还没有引导起来。下面,我们摘一些段落进行分析。

字符串

字符串是组成脚本的基本语义单元。字符串在脚本中可以表示函数名、变量等。

保留字是一类特殊的字符串,在脚本中具有特别的意义,不能表示函数名、变量等。grub.cfg定义的一些保留字如下:

! [[ ]] { }

case do done elif else esac fi for function

if in menuentry select then time until while

变量引用

和Shell脚本一样,grub.cfg用”$”表示一个变量。变量可放在大括号中,如
${prefix}
,其主要目的是在与字符联接时能被完整的识别为一个变量。如表达式
if [ -e ${prefix}/gfxblacklist.txt ]; then
${prefix}
可以正确被识别为一个变量。一般的变量的名称是以字母开头的,后面可以有0个或者多个字母。数字作为变量名表示函数中的参数。如
$1
表示传递给函数的第一个变量。变量
$?
表示近一次调用函数的状态值。这个含义也与Shell中的含义相同。

其它预定义特殊的变量。总结如下表:

变量名称含义
$1
传到函数中的第一个变量,以此类推
$?
近一次函数执行的状态值,可参考Shell中的状态值的含义
$@
所有的参数,每个参数用”“引起来
$*
没加引号的所有参数
$#
参数的个数

命令

一行简单的命令就是一个表达式。他可以是一个单独的命令执行。第一个单词指定了要执行的命令。后面的字符串表示其执行所需要的参数。命令执行的返回值存放在状态值里面,在接下来最近语句中可以使用
$?
获取。可以在命令执行前加上”!”,使其最后的值取反。

Grub配置很多引导过程所需要的命令。这些命令在脚本中的使用方式和惯例与Shell脚本非常相似。可以说,Grub配置文件本身就是一个小的程序解释器。

官方的手册将其所支持的命令分根据应用范围和作用,分了四类:

1. 与菜单相关的命令

Grub支持
menuentry
submenu
两个与启动项相关的命令。

menuentry
表示生成一个GRUB的选择项,一旦用户选择这个选项后,将会执行一系列指定的命令。

submenu
生成一个包括子菜单的菜单项。用户选定后,会弹出子菜单,子菜单的中的选项由
menuentry
指定。

2. 通用命令

通用命令是可以放在配置文件任何位置命令。

3. 可以在命令行及菜单内部使用的命令

这类命令即可以在命令行也可以在菜单项内部执行。此类命令可以分成两类理解。一类是模访Shell用于判断和字符串处理的基本命。如
[
cat
cmp
等。一类是Grub所使用的命令集。如
acpi
linux
boot
等。

Grub提供了
help
命令可查阅各个命令的具体使用。

4. 与网络相关指令

此类命令用于远程启动时对网络环境配置。如设置IP、设置DHCP等。

函数

形式为:

function name { command; . . . }


表示定义了一个函数名为
name
的函数;函数体中包含着这个函数所要执行的命令。其格式如简单命令所述。通常的写法是每一行一个命令,如果在同一行中,两个命令之间需要用分号隔开。函数定义不会改变
$?
中的值。即在函数定义块之前和之后,
$?
的值一定是相等。函数调用时,其状态值由最后一个执行的命令所决定。

以下为一个函数的示例:

function load_video {
if [ x$feature_all_video_module = xy ]; then
insmod all_video
else
insmod efi_gop
insmod efi_uga
insmod ieee1275_fb
insmod vbe
insmod vga
insmod video_bochs
insmod video_cirrus
fi
}


语句控制块

可参考
shell
中的写法,如下表:

表达式含义
for name in word . . .; do list; done
对word中的每个字符串进行遍历
if list; then list; [elif list; then list;] . . . [else list;] fi
条件判断
while cond; do list; done

until cond; do list; done
循环
menuentry title [‘--class=class’ . . .] [‘--users=users’] [‘--unrestricted’]

[‘--hotkey=key’] [‘--id=id’] { command; . . . }
定义一个启动项

环境块

为了简化引导程序的实现,Grub没有实现对文件写的逻辑。因此,在操作系统运行之前,我们不能像管理配置文件那样对一些需要保存的信息进行保存的。在一些场景下,我们需要保存一些用户在Grub操作中执行的一些状态信息。例如,保存本次用户选择的启动项,以便下次启动时以此为启动项。为此,Grub引入了环境块(environment block)来解决这个问题。在启动过程中,grub.cfg调用load_env命令将其载入到内存中,在运行过程中,使用save_env命令保存修改的信息。

在ubuntu系统上,环境块存储于/boot/grub/grubenv中。我们可以使用grub-editenv编辑这个文件。ubuntu系统默认情况下,对没有保存任保变量。例用上述命令增加一个变量:

[root@ubuntu grub]# grub-editenv  /boot/grub/grubenv set foo=1


查看验证:

[root@ubuntu grub]# grub-editenv  /boot/grub/grubenv list
foo=1


取消这个已经设置的变量:

[root@ubuntu grub]# grub-editenv  /boot/grub/grubenv unset foo
[root@ubuntu grub]# grub-editenv  /boot/grub/grubenv list


grub-mkconfig利用此信息定义记录上次启动信息以便下次利用。当将设置选项2 设GRUB_SAVEDEFAULT设置为true后,再次生成一次配置文件,启动时,就能够将启动时选择的启动项记录到环境块的saved_entry。为使下次执行仍使用上次启动的项,还需要将GRUB_SAVEDEFAULT设置成saved

如果grub环境变量
GRUB_SAVEDEFAULT
设置为true时,表示:每当启动时,对启动项进行的调整,这个启动项将会保存起来。

生成配置文件

命令说明

Grub2运行时配置文件可以通过grub-mkconfig3进行生成。

[root@ubuntu grub]# grub-mkconfig -o /tmp/grub.cfg
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-3.16.0-30-generic
Found initrd image: /boot/initrd.img-3.16.0-30-generic
Found memtest86+ image: /boot/memtest86+.elf
Found memtest86+ image: /boot/memtest86+.bin
done


grub-mkconfig检索配置项

该命令被调用时会检索/etc/grub.d目录下配置文件,按照文件排序进行调用执行,将配置文件的输出到标准输出的信息重定向到grub.cfg中。在该目录下的所有可执行的配置文件都会被调用,为确保调用顺序易理解,系统将该目录下的文件进行编号。编号的规则是:

* 所有的需要被生成命令识别的配置文件都以*‘编号’+‘-’+文件名的形式定义

* 编号00_*: 留给00_header,使其优先于所有的配置文件而被执行

* 编号10_*: 本系统的相关启动项生成脚本

* 编号20_*: 第三方应用的生成脚本

通过上述编码之后,每个配置文件名称前面的编号基本上决定了其解析顺序。这个顺与执行
ls -l
所获得的文件的顺序是一致的。如:unbuntu系统中的配置文件列出如下:

[root@ubuntu grub.d]# ls -l
total 76
-rwxr-xr-x 1 root root  9791 Jul 16 22:59 00_header
-rwxr-xr-x 1 root root  6058 May  8  2014 05_debian_theme
-rwxr-xr-x 1 root root 11608 May 15  2014 10_linux
-rwxr-xr-x 1 root root 10412 May 15  2014 20_linux_xen
-rwxr-xr-x 1 root root  1992 Mar 12  2014 20_memtest86+
-rwxr-xr-x 1 root root 11692 May 15  2014 30_os-prober
-rwxr-xr-x 1 root root  1418 Jul 16 22:59 30_uefi-firmware
-rwxr-xr-x 1 root root   214 May 15  2014 40_custom
-rwxr-xr-x 1 root root   216 May 15  2014 41_custom
-rw-r--r-- 1 root root   483 May 15  2014 README


如果需要对Grub进行个性化定制,我们需要调整/etc/grub.d目录中的配置。

在软件RAID上引导Linux https://www.experts-exchange.com/questions/28555676/Centos-7-grub2-problems-putting-root-on-raid1-md-array.html
/etc/default/grub 中的配置项是通过grub-mkconfig解析。为使其生效,改动后均需要重新生成grub配置文件/boot/grub/grub.cfg
centos 中为了区分新一代的Grub命令需要将Grub替换成grub2
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  操作系统 bootloader