三、Boot loader: Grub2

1.Boot loader的两个stage

之前讲过,在BIOS读取完信息后,就是到第一个开机设备的MBR去读取boot loader,这个boot loader有菜单功能、直接载入核心文件以及控制权移交的功能等。系统必须要有loader才有办法加载操作系统的核心,但是我们都知道,MBR是整个硬盘的第一个sector内的一块,充其量也就446bytes,即使GPT也没有很大空间存储loader的数据,那么我们的loader是如何安装呢?

为了解决这个问题,Linux把boot loader的源码执行与设定值加载分为了两个阶段来执行:

(1)STAGE 1:执行boot loader主程序。这个主程序必须要安装在开机区,也就是MBR或者boot sector。因为MBR太小,所以MBR或boot sector通常只安装boot loader的最小主程序,并没有安装loader的相关配置文件。

(2)STAGE 2:主程序载入配置文件。通过boot loader加载所有配置文件与相关的环境参数配置文件(包括文件系统定义与主要配置文件grub.cfg)。一般来说,配置文件都在/boot下面。我们查看一下grub下面就可以了,ubuntu目录为/boot/grub,Cent OS 7.x为/boot/grub2。如图3.1所示。

图3.1 grub里面的文件

root@xiaoao-virtual-machine:/home/xiaoao# ls -l /boot/grub/i386-pc

总用量 2440

-rw-r--r-- 1 root root   7972 9月   5 22:08 915resolution.mod

-rw-r--r-- 1 root root  10620 9月   5 22:08 acpi.mod

-rw-r--r-- 1 root root   1324 9月   5 22:08 adler32.mod

-rw-r--r-- 1 root root   5652 9月   5 22:08 affs.mod

-rw-r--r-- 1 root root   6108 9月   5 22:08 afs.mod

-rw-r--r-- 1 root root  15540 9月   5 22:08 ahci.mod

-rw-r--r-- 1 root root    492 9月   5 22:08 all_video.mod

-rw-r--r-- 1 root root   1036 9月   5 22:08 aout.mod

-rw-r--r-- 1 root root   2956 9月   5 22:08 archelp.mod

-rw-r--r-- 1 root root   5636 9月   5 22:08 ata.mod

-rw-r--r-- 1 root root   4284 9月   5 22:08 at_keyboard.mod

-rw-r--r-- 1 root root   1640 9月   5 22:08 backtrace.mod

-rw-r--r-- 1 root root   6816 9月   5 22:08 bfs.mod

-rw-r--r-- 1 root root   4692 9月   5 22:08 biosdisk.mod

-rw-r--r-- 1 root root   2256 9月   5 22:08 bitmap.mod

-rw-r--r-- 1 root root   3788 9月   5 22:08 bitmap_scale.mod

-rw-r--r-- 1 root root   2128 9月   5 22:08 blocklist.mod

-rw-r--r-- 1 root root    512 9月   5 22:08 boot.img

-rw-r--r-- 1 root root   2432 9月   5 22:08 boot.mod

(以下全部省略)

 

2.grub2的配置文件grub.cfg初探

安装在MBR的grub2的主程序最重要的任务是从硬盘中加载核心文件,让核心能够顺利驱动整个系统的硬件,所以grub2必须认识硬件才可以。grub2对硬件的代码设置与传统Linux硬盘代号完全不同,rub2对硬盘的识别是用以下代码:

(hd0,1)       #一般的默认语法,由grub2自动判断分割格式

(hd0,msdos1)#此磁盘的分割为传统的MBR模式

(hd0,gpt1)#此磁盘的分割为GPT模式

这里面主要的是:

①硬盘代号以小括号()包起来;

②硬盘以hd表示,后面会接一组数字;

③以『搜寻顺序』做为硬盘的编号:第一个搜寻到的硬盘为0号,第二个为1号,以此类推;

④每个硬盘的第一个partition代号为1,依序类推。

如上所示,第一个搜索到的硬盘代号为(hd0),而该硬盘的第一个分割槽为1。当然,为了区分不同的分割格式,1前面会加上gpt或者msdos等标识。要记住,硬盘变好从0开始,分割槽编号从1开始

(1)/boot/grub2/grub.cfg配置文件(不可修改

在grub.cfg最开始的部份,其实大多是环境设定与默认值设定等,比较重要的当然是默认由哪个选项开机(set default)以及预设的秒数(set timeout),再来则是每一个菜单的设定,就是在『menuentry』这个设定值之后的项目。

在menuentry之后会有几个项目的规范,包括『--class,--unrestricted --id』等等的指定项目,之后通过『{}』将这个菜单会用到的数据框起来,在选择这个菜单之后就会进行括号内的动作的意思。如果真的点选了这个菜单,那grub2首先会加载模块。之后就是三个比较重要的项目:

①set root='hd0,gpt2'

这里的root是指定grub2配置文件所在的那个设备。以我们的测试机来说,当初安装的时候分割出/与/boot两个设备,而grub2是在/boot/grub2这个设备上,而这个位置的磁盘文件名为/dev/vda2,因此完整的grub2磁盘名称就是(hd0,2)。因为我们的系统用的是GPT的磁盘分区格式,因此全名就是『hd0,gpt2』。

②linux16 /vmlinuz-... root=/dev/mapper/centos-root ...

这个就是 Linux 核心文件以及核心执行时所下达的参数。你应该会觉得比较怪的是,我们的核心文件不是 /boot/vmlinuz-xxx 吗? 怎么这里的设定会是在根目录呢?这个跟上面的 root 有关啦!大部分的系统大多有 /boot 这个分割槽,如果 /boot 没有分割, 那会是怎么回事呢?我们用底下的迭代来说明一下:

如果没有 /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... 之类的模式喔!

③initrd16 /initramfs-3.10...

这个就是initramfs所在的檔名,跟linux16那个vmlinuz-xxx相同,这个档名也是需要搭配『se troot=xxx』那个项目的装置,才会得到正确的位置。

 

3.grub2配置文件维护/etc/default/grub/etc/grub.dgrub,40_custom

我们可以通过/etc/default/grub这个主要环节配置文件与/etc/grub.d/目录中的相关配置文件来处理比较好。

(1)/etc/default/grub环境配置文件

我们查看一下这个文件,执行“cat /etc/default/grub”,可以得到如图3.2所示的情况。

图3.2 /etc/default/grub环境配置文件情况

如下所示是CentOS 7.x的情况:

[root@study ~]# cat /etc/default/grub

GRUB_TIMEOUT=5            # 指定预设倒数读秒的秒数

GRUB_DEFAULT=saved         # 指定预设由哪一个菜单来开机,预设开机菜单之意

GRUB_DISABLE_SUBMENU=true    # 是否要隐藏次菜单,通常是藏起来比较好。

GRUB_TERMINAL_OUTPUT="console"

#指定数据输出的终端机格式,默认通过文字终端机

GRUB_CMDLINE_LINUX="rd.lvm.lv=centos/root rd.lvm.lv=centos/swap crashkernel=auto rhgb quiet"             # 就是在 menuentry 括号内的 linux16 项目后续的核心参数

GRUB_DISABLE_RECOVERY="true"     # 取消修复菜单的制作

主要几个项目解释如下:

①倒数时间参数:GRUB_TIMEOUT

这个设定值相当简单,后面就是接你要倒数的秒数即可。如果不想等待则输入0,如果一定让用户选择,则填-1即可。

②是否隐藏菜单项目:GRUB_TIMEOUT_STYLE

这个项目可选择的设定值有menu, countdown, hidden等等。如果没有设定,预设是menu的意思。这个项目主要是在设定要不要显示菜单!如果你不想要让使用者看到菜单,这里可以设定为countdown。countdown会在屏幕上显示剩余的等待秒数,而hidden则空空如也。

③信息输出的终端机模式:GRUB_TERMINAL_OUTPUT

这个项目是指定输出的画面应该使用哪一个终端机来显示的意思,主要的设定值有『console, serial, gfxterm, vga_text』等等。除非有特别的需求,否则一般使用console即可。

④默认开机菜单项目:GRUB_DEFAULT

这个项目在指定要用哪一个菜单(menuentry)来作为默认开机项目的意思。能使用的设定值包括有『saved,数字,title名,ID名』等。

⑤核心的外加参数功能:GRUB_CMDLINE_LINUX:在这里加入你的核心在启动的时候还需要加入的其它参数。

在修改完成后,直接用grub2-mkconfig来重建grub.cfg即可。

(2)菜单建置的脚本/etc/grub.d/*

这里面的几个文件主要有以下几个:

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:如果你还有其他想要自己手动加上去的选单项目,或者是其他的需求,那么在这里补充。

所以一般来说,我们修改的时候用到的就是40_custom这个文件。我们知道menuentry是一个菜单,那么后续的项目有哪些呢?常见有这几样:

①直接指定核心开机;

②通过chainloader的方式移交loader控制权。

4.initramfs的重要性与新建initramfs文件:dracut/mkinitrd

initramfs内所包含的模块大多是与开机过程有关,而主要以文件系统及硬盘模块(如usb,SCSI等)为主。

一般来说,需要initramfs的时候为:

①根目录所在硬盘为SATA、USB或SCSI等连接接口;

②根目录所在文件系统为LVM、RAID等特殊格式;

③根目录所在文件系统为非传统Linux可识别的文件系统时;

④其他必须要在核心加载时提供的模块。

一般来说,各distribution提供的核心都会附上initramfs文件,但如果你有特殊需要所以想重制initramfs文件的话,可以使用dracut或mkinitrd来处理的。这个文件的处理方式很简单,mandracut或manmkinitrd就知道了。

下面来介绍下dracut功能。

[root@study ~]# dracut [-fv] [--add-drivers 列表] initramfs文件名 核心版本

选项与参数:

-f:强迫编译出initramfs,如果initramfs文件已经存在,则覆盖掉旧文件。

-v:显示dracut的运作过程。

--add-drivers列表:在原本的默认核心模块中,增加某些你想要的模块!模块位于核心所在目录/lib/modules/$(uname-r)/kernel/*。

initramfs文件名:就是你需要的文件名。开头最好就是initramfs,后面接版本与功能。

核心版本:预设当然是目前运作中的核心版本,不过你也可以手动输入其他不同版本。

其实dracut还有很多功能,例如底下的几个参数也可以参考看看:

--modules:将dracut所提供的开机所需模块(核心模块组)加载,可用模块在底下的目录内/usr/lib/dracut/modules.d/;

--gzip|--bzip2|--xz:尝试使用哪一种压缩方式来进行initramfs压缩。预设使用gzip;

--filesystems:加入某些额外的文件系统支持。

 

5.测试与安装grub2grub2-install

加入你的Linux并非使用grub2,那么就需要使用grub-install将一些必要的文件复制到/boot/grub2里面去。

要安装的话,需要这样做:

[root@study ~]# grub2-install [--boot-directory=DIR] INSTALL_DEVICE

选项与参数:

--boot-directory=DIR那个DIR为实际的目录,使用grub2-install预设会将grub2所有的档案都复制到/boot/grub2/*,如果想要复制到其他目录与装置去,就得要用这个参数。INSTALL_DEVICE 就是安装的装置代号。

最后总结一下:

①如果是从其他bootloader转成grub2时,得先使用grub2-install安装grub2配置文件;

②承上,如果安装到partition时,可能需要加上额外的许多参数才能够顺利安装上去;

③开始编辑/etc/default/grub及/etc/grub.d/*这几个重要的配置文件;

④使用grub2-mkconfig-o/boot/grub2/grub.cfg来建立开机的配置文件。

 

6.关于开机画面与终端机画面的图形显示方式

如果你想要让你的开机画面使用图形显示方式,例如使用中文来显示你的画面,因为我们预设的locale语系就是zh_CN.utf8嘛!所以理论上grub2会显是中文出来才对啊。有没有办法达成呢?是有的~通过图形显是的方法即可!不过,我们得要重新修改grub.cfg才行喔!依据底下的方式来处理:

# 先改重要的配置文件

[root@study ~]# vim /etc/default/grub

.....(前面省略).....

GRUB_TERMINAL=gfxterm       # 设定主要的终端机显示为图形界面

GRUB_GFXMODE=1024x768x24    # 图形界面尺寸

GRUB_GFXPAYLOAD_LINUX=keep  # 保留图形界面,不要使用text。

 

# 重新建立配置文件

[root@study ~]# grub2-mkconfig -o /boot/grub2/grub.cfg

 

8.为个别菜单加上密码:grub2-mkpasswd-pbkdf2

grub2的菜单管理中,有针对两种身份进行密码设置:

superusers:设定系统管理员与相关参数还有密码等,使用这个密码的用户,将可在grub2内具有所有修改的权限。但一旦设定了这个superusers的参数,则所有的指令修改将会被变成受限制的。

users:设定一般账号的相关参数与密码,可以设定多个用户喔!使用这个密码的用户可以选择要进入某些选单项目。不过,菜单项目也得要搭配相对的账号才行喔!(一般来说,使用这种密码的账号并不能修改菜单的内容,仅能选择进入菜单去开机而已)。

(1)grub2密码设定的文件位置与加密的密码

记得我们在前几小节谈到主要的环境设定是在/etc/grub.d/*里面吧?里面的档案文件名有用数字开头,那些数字照顺序,就是grub.cfg的来源顺序了。因此最早被读的应该是00_header,但是那个档案的内容挺重要的,所以CentOS7不建议你改它~那要改谁?就自己建立一个名为01_users的档案即可!要注意是两个数字开头接着底线的档名才行,然后将账号与密码参数给它补进去。

(2)为个别的菜单设置账号密码的使用模式

现在我们假设在40_custom里面要增加一个可以进入救援模式(rescue)的环境,并且放置到最后一个选单中,同时仅有知道dmtsai的密码者才能够使用,那你应该这样作:

[root@study ~]# vim /etc/grub.d/40_custom

.....(前面省略).....

menuentry 'Rescue CentOS7, with Linux 3.10.0-229.el7.x86_64' --users dmtsai {

load_video

set gfxpayload=keep

insmod gzio

insmod part_gpt

insmod xfs

set root='hd0,gpt2'

if [ x$feature_platform_search_hint = xy ]; then

search --no-floppy --fs-uuid --set=root --hint='hd0,gpt2'  94ac5f77-cb8a-...

else

search --no-floppy --fs-uuid --set=root 94ac5f77-cb8a-495e-a65b-2ef7442b837c

fi

linux16 /vmlinuz-3.10.0-229.el7.x86_64 root=/dev/mapper/centos-root ro rd.lvm.lv

=centos/root rd.lvm.lv=centos/swap crashkernel=auto rhgb quiet

systemd.unit=rescue.target

initrd16 /initramfs-3.10.0-229.el7.x86_64.img

}

 

[root@study ~]# grub2-mkconfig -o /boot/grub2/grub.cfg

最后一步当然不要忘记重建你的grub.cfg啰!然后重新启动测试一下。