在开机过程中 boot loader 是载入内核的重要工具,如果没有 boot loader ,那么 kernel 就无法被系统加载。
目前新版的 CentOS 7.x 已经将沿用多年的 grub 换成了 grub2,并且 grub2 版本在设定与安装上跟先前的 grub 有一定的差异。
boot loader 的两个 stage
开机流程中,在 BIOS 读完信息后,接下来会到第一个开机装置的 MBR 读取 boot loader 。这个 boot loader 可以具有菜单选择功能、直接加载内核文件以及控制权移交的功能等, 系统必须要有 loader 才能加载该操作系统的内核。
另外,MBR 是整个硬盘的第一个 sector 内的一个区块,整个大小为 446 bytes。即使是 GPT 也没有很大的扇区来储存 loader 的数据。 仅仅是程序代码和设定数据不可能只占据这么小的容量。
为了解决这个问题,所以 Linux 将 boot loader 的程序代码执行与设定值加载分成两个阶段 (stage) 来执行:
Stage 1:执行 boot loader 主程序:
第一阶段:执行 boot loader 的主程序,这个主程序必须要被安装在开机区,也就是 MBR 或者是 boot sector 。而因为 MBR 容量太小,所以 MBR 或 boot sector 通常仅安装 boot loader 的最小主程序, 并没有安装 loader 的相关配置文件;
Stage 2:主程序加载配置文件:
第二阶段:通过 boot loader 加载所有配置文件与相关的环境参数文件 (包括文件系统定义与主要配置文件 grub.cfg),一般情况下,配置文件都在 /boot
目录下。
与 grub2 有关的文件都存放在了 /boot/grub2
中:
1 | [root@m5 ~]# ls /boot/grub2/ --format=single-column |
上面只列出了部分模块说明,loader 读取了这些文件系统定义数据后,就能够认识文件系统并读取在该文件系统内的内核文件了。
grub2 的配置文件 /boot/grub2/grub.cfg
grub2 的优点包括:
识别与支持较多的文件系统,并且可以使用 grub2 的主程序直接在文件系统中搜索内核文件;
开机的时候,可以自定义与修改开机设定条目,类似 bash 的命令模式;
可以动态搜索配置文件,而不需要在修改配置文件后重新安装 grub2 。即:只要修改完
/boot/grub2/grub.cfg
中的设定,下次开机就会生效了。
这也是 Stage 1, Stage 2 分别安装在 MBR (主程序) 与文件系统当中 (配置文件) 的原因
磁盘与分区在 grub2 中的代号
安装在 MBR 的 grub2 主程序,最重要的任务之一是从磁盘当中加载内核文件, 以让内核能够顺利驱动整个系统的硬件。所以 grub2 必须要识别硬盘,而 grub2 对硬盘的代号设置与传统的 Linux 磁盘代号可完全不同的,它对硬盘的识别使用的是如下的代号:
1 | (hd0,1) # 一般的默认语法,由 grub2 自动判断分区格式 |
要注意以下几点:
硬盘代号以小括号
()
包起来;硬盘以 hd 表示,后面会接一组数字;
以 搜索顺序 作为硬盘的编号;
第一个搜索到的硬盘为 0 号,第二个为 1 号,以此类推;
每块硬盘的第一个 partition 代号为 1 ,以此类推。
因此,第一块 搜索到的硬盘 代号为:(hd0)
,而这块硬盘的第一号分区为 (hd0,1)
。另外,为了区分不同的分区格式,磁盘后面的分区号码可以使用类似 msdos1 与 gpt1 的方式来调整。 一定要注意:磁盘的号码是由 0 开始编号,分区的号码则与 Linux 一 样,是由 1 号开始编号。
由于 BIOS 可以调整磁盘的开机顺序,因此上述的磁盘对应的 (hdN) 中的 N 是可能会变动的。因此,整个硬盘代号为:
磁盘搜索顺序 | 在 Grub2 中的代号 |
---|---|
第一块(MBR) | (hd0) (hd0,msdos1) (hd0,msdos2) (hd0,msdos3) …… |
第二块(GPT) | (hd1) (hd1,gpt1) (hd1,gpt2) (hd1,gpt3) …… |
第三块 | (hd2) (hd2,1) (hd2,2) (hd2,3) …… |
第一块硬盘的 MBR 安装处的硬盘代号是 (hd0)
, 第一块硬盘的第一个分区的 boot sector 代号就是 (hd0,msdos1)
,那么第一块硬盘的第一个逻辑分区的 boot sector 代号就是 (hd0,msdos5)
。
假设系统仅有一块 SATA 硬盘,那么该硬盘的第一个逻辑分区槽在 Linux 与 grub2 当中的档名与代号应该是 (hd0,msdos5)
或 (hd0,5)
。
/boot/grub2/grub.cfg 配置文件
1 | [root@m5 ~]# cat /boot/grub2/grub.cfg |
在文件最开始的部分,大多是环境设定与默认值设定等,更重要的是默认由哪个选项开机 (set default
) 以及预设的秒数 (set timeout
), 然后是每一个选择菜单的设定,也就是在 menuentry 这个设定值之后的配置。
在 menuentry 之后会有几个配置的规范,包括 --class
, --unrestricted
--id
等等的指定配置,之后通过 {}
将这个选择菜单会用到的数据包起来,在选择这个菜单之后就会进行括号内的动作。 如果真的选择了这个菜单,那么 grub2 首先会加载模块:
1 | load_video |
之后就是三个比较重要的配置:
1 | set root='hd0,msdos1' |
使用 root 来指定 grub2 配置文件所在的硬盘位置和分区。比如,在安装系统的时候分区出 /
与 /boot
两个分区,而 grub2 是在 /boot/grub2
这个位置上, 这个位置的磁盘文件名为 /dev/vda2
,因此完整的 grub2 磁盘名称就是 (hd0,2)
。如果系统用的是 GTP 的磁盘分区格式, 全名就是 (hd0,gpt2)
。
1 | linux16 /vmlinuz-3.10.0-957.el7.x86_64 root=UUID=7f64a74e-d812-4042-b4ff-14b7e4bc36ec ro crashkernel=auto biosdevname=0 net.ifnames=0 rhgb quiet LANG=en_US.UTF-8 |
这个配置是 Linux 内核文件以及内核在执行时所下达的参数。这里的内核文件位置和上面的 root 有关:
如果没有
/boot
分区,仅有/
分区,文件路径会这样变化:/boot/vmlinuz-xxx
=>(/)/boot/vmlinuz-xxx
=>(hd0,msdos1)/boot/vmlinuz-xxx
如果
/boot
是独立分区,则文件路径的变化会是这样:/boot/vmlinuz-xxx
=>(/boot)/vmlinuz-xxx
=>(hd0,msdos1)/vmlinuz-xxx
因此,在 linux16 后面接的文件路径要和上面的 root 搭配在一起,才是完整的绝对路径文件名。而 linux16 /vmlinuz-xxx root=/file/name
中的 root 指的是在 linux 文件系统中,根目录的位置。在开机流程中,内核会主动去挂载根目录,并且从根目录中读取配置 文件, 再进一步开始开机流程。 所以,内核文件后面一定要配置根目录的位置。
从 /etc/fstab
里面可以知道根目录的挂载可以是设备文件名、 UUID 与 LABEL 名称,因此这个 root 后面也是可以带入类似 root=UUID=1111.2222.33...
的模式。
1 | initrd16 /initramfs-3.10.0-957.el7.x86_64.img |
这一行配置的是 initramfs 所在的位置,与 linux16 vmlinuz-xxx 相同,这条配置也需要搭配 set root=xxx
才会得到正确的位置。
grub2 配置文件维护 /etc/default/grub 与 /etc/grub.d
grub2 的主配置文件 grub.cfg 因为该文件的内容太过复杂,数据量非常庞大,grub2 官方说明不建议手动修改。而应该要通过 /etc/default/grub
这个环境配置文件与 /etc/grub.d/
目录内的相关配置文件来处理。
/etc/default/grub 环境配置文件
配置文件大致内容如下:
1 | GRUB_TIMEOUT=5 # 预设倒计时:经过多少秒之后进入开机 |
1 | GRUB_TIMEOUT=5 |
这个选项用来配置倒计时的秒数,如果不想等待则输入 0 ,如果一定要使用者选择,则填 -1 即可。
1 | GRUB_TIMEOUT_STYLE=menu |
这个选项可选择的设定值有 menu, countdown, hidden 等。如果没有设定,预设是 menu 。 主要用于设置是否显示选择菜单。如果不想要让使用者看到选择菜单,则可以设定为 countdown。使用 countdown 会在屏幕上显示剩余的等待秒数, 而 hidden 则什么都不显示。
1 | GRUB_TERMINAL_OUTPUT="console" |
这个选项用来配置输出的画面应该使用哪一个终端来显示,主要的设定值有console, serial, gfxterm, vga_text 等。 除非有特别的需求,否则一般使用 console 即可。
1 | GRUB_DEFAULT=saved |
这个选项用来指定使用哪一个选择菜单 (menuentry) 来作为默认开机条目。能使用的设定值包括有 saved, 数字, title 名, ID名 等。 假设当前系统有三个 menuentry 的条目大约像这样:
1 | menuentry 'CentOS Linux (4.13.2-1.el7.elrepo.x86_64) 7 (Core)' --class centos --class gnu-linux |
几个常见的设定值是这样的:
1 | GRUB_DEFAULT=1 |
1 | GRUB_CMDLINE_LINUX="crashkernel=auto rhgb quiet" |
如果想要使内核在启动的时候入额外的参数,就可以使用这个选项来配置。比如对于云主机来说,并不关心网卡的物理位置,只想将网卡命名方式改为 eth0
、eth1
的格式,可以这样处理:
1 | GRUB_CMDLINE_LINUX="...... net.ifnames=0 biosdevname=0" |
这个环境配置文件编写完成之后,必须要使用 grub2-mkconfig 来重建 /boot/grub2/grub.cfg
才有作用。
1 | grub2-mkconfig -o /boot/grub2/grub.cfg |
选单建立的脚本 /etc/grub.d/*
在 grub2-mkconfig 执行时,会分析 /etc/grub.d/
下的文件,然后执行各个脚本,最后生成 grub.cfg。该目录下会有这些文件存在:
00_header:主要在建立初始的显示项目,包括需要加载的模块分析、屏幕终端的格式、倒数秒数、选单是否需要隐藏等,大部分在
/etc/default/grub
里所设定的变量,基本都会在这个脚本当中被利用来重建 grub.cfg 。10_linux:分析 /boot 下的文件,尝试找到正确的 linux 内核以及读取这个内核需要的文件系统模块与参数等,这一系列操作都在这个脚本运行时进行,并设定到 grub.cfg 当中。 因为这个脚本会将所有在 /boot 下的每一 个内核文件都对应到一个选单,因此核心文件数量越多,开机选单项目就越多。 如果不想要旧的核心出现在选单上,可以通过移除旧核心来处理。
30_os-prober:这个脚本默认会到系统上找其他的 partition 里面可能含有的操作系统,然后将该操作系统做成选单。 如果不想要让其他的操作系统被侦测到并拿来开机,可以在
/etc/default/grub
里加上GRUB_DISABLE_OS_PROBER=true
取消这个脚本的运行。40_custom:如果有其他需要自定义加上去选单条目,或者是其他需求,建议在这里补充。