bootloader及grub概述


    启动引导装载程序(bootloader),就是系统启动时在执行POST加电自检功能的BIOS程序之后、操作系统内核运行之前这中间所运行的一段程序,bootloader的主要作用是加载内核,并将整个系统的控制权移交给内核。常见的bootloader有Windows的ntloader、Linux的lilo、grub及grub2等。其中,在CentOS 5/6系列上使用的是grub,而在CentOS 7开始则使用grub;lilo则常见于手机,因此lilo是使用者最多的一种bootloader。


Windows:

    ntloader

Linux:

    LILO:LInux LOader

    GRUB:Grand Unified Bootloader

        GRUB 0.X:Grub Legacy

        GRUB 1.X:Grub2


    就MBR分区方法而言,大多数Bootloader都分为多个阶段来引导,其中第一阶段存放于MBR中,用于引导装载第二阶段。而grub作为Bootloader的一种也不例外,它是分为stage1,stage2,stage1.5这三个阶段来引导的:stage1的主要工作是加载stage1.5,stage1.5的主要工作是加载文件系统驱动,让stage1中的bootloader能够识别stage2所在分区的上的文件系统,stage1.5会提供多种文件系统的驱动,如下:

[[email protected] grub]# ls -1F
device.map
e2fs_stage1_5
fat_stage1_5
ffs_stage1_5
grub.conf
iso9660_stage1_5
jfs_stage1_5
[email protected]
minix_stage1_5
reiserfs_stage1_5
splash.xpm.gz
stage1
stage2
ufs2_stage1_5
vstafs_stage1_5
xfs_stage1_5
[[email protected] grub]#

    可以看见,每个stage1.5文件对应一种文件系统,而借助于stage1.5阶段所提供的文件系统驱动,stage1就可以通过加载其中某一种文件系统驱动来识别stage2所在分区的文件系统了。举个例子,如果stage2所在分区上的文件系统是ext4,那么stage1就会通过stage1.5提供的e2fs_stage1_5来识别stage2。完成对stage2的识别和加载之后,stage2就能够加载stage2所在分区上(即/boot所在分区)的内核文件和ramdis文件(注意:ramdisk文件并非必须)至内存中了。此外,grub的功能主要是通过stage2来实现的,在系统启动时,stage2可以向用户提供菜单,并提供交互式接口,允许用户通过菜单选择要加载的内核或操作系统,另外,如果用户有需要,stage2还可以为菜单提供保护机制,而保护机制有两种,一种是为编辑菜单提供认证,另外一种是为启动的内核或操作系统提供认证。以下总结grub(stage2)的主要功能:


grub(stage2)的功用:

(1)向用户提供菜单,并提供交互式接口。例如,在启动时可进入菜单,通过键入e键进入编辑模式,可以编辑菜单,此时会读取配置文件;也可以通过键入c键可以进入命令行模式,即进入交互式接口,可以自己手动指定要启动的内核而不读取grub配置文件;

(2)加载用户选择的内核或操作系统。在选择时允许用户传递参数给内核,也可以隐藏菜单的具体内容;

(3)为菜单提供保护机制。可以为编辑菜单提供认证,用户需要通过认证方可编辑指定菜单;也可以为内核或操作系统提供认证,用户需要通过认证方可启动指定的内核或操作系统;



演示:

启动时进入编辑菜单:

grub应用


键入e键可以编辑菜单,如下所示,再此时再键入一次e键就可以编辑选定的参数了。

grub应用


按下Esc键可以返回第一个菜单界面,再键入c键可以直接进入命令行模式(交互式接口):

grub应用


通过'help'可查看帮助,而'help KEYWORD'则可以查看指定项的详细帮助:

grub应用


grub应用


可以通过find命令查找文件,例如查找/boot分区下的内核文件:

grub应用


用户可以通过这个交互式接口指定要加载的内核及ramdisk文件,但在此之前要先指定grub的根分区(注意:stage2所在分区即为grub的根分区,这里grub的根不一定是文件系统的根),而要指定grub的根分区则必须采用grub识别设备的方式来指定,grub识别设备是通过命令'root (hd#,#)'来指定的,各部分解释如下:

root:用于切换grub所在分区;
hd#:磁盘编号,用数字表示;从0开始编号;
#:分区编号,用数字表示;从0开始编号;

需要注意的是,用户在指定要加载的内核或操作系统时,通常需要指定grub的根(root (hd#,#)),并且指定Kernel及initrd所在路径,必要时传递一些参数给Kernel。但是,Kernel及initrd是以grub的“根”作为起始目录的,也就是说如果/boot作为基本磁盘分区独立出来时,那么Kernel及initrd在指定路径时的起始目录为'/';如果/boot不作为基本磁盘分区,而是根分区('/')之下的一个目录时,那么此时grub的“根”即为文件系统的根,Kernel及initrd的起始目录为/boot/。


用户可在交互式接口下手动指定要加载的Kernel及initrd。在进入菜单时和刚才一样,键入c键进入grub命令行模式(交互式接口),而当/boot为独立分区时,分别指定kernel及initrd的在grub的“根”下的文件路径,如下所示:

grub应用

这里在Kernel指定路径之后至少还要传递两个参数,一个是'ro',指明是以只读方式挂载根文件系统,'root=xxx'这一样则指明根文件系统所在分区,这里实验环境的根文件系统所在分区是一个LVM逻辑卷。最后键入'boot'即可加载指定的内核或操作系统并完成启动。


grub的配置文件:/boot/grub/grub.conf <--- /etc/grub.conf(链接文件)


配置项:

defaults=#:设定默认要启动的菜单项;菜单项(title)从0开始编号;

timeout=#:设定菜单项等待用户选择的时长;

splashp_w_picpath=(hd#,#)/PATH/TO/XPM_PIC_FILE:指明菜单项背景图片的文件路径;

hiddenmenu:隐藏菜单;

password [--md5] STRING:菜单编辑认证;

title TITLE:定义菜单项“标题”;可出现多次;

    root (hd#,#):grub查找stage2及Kernel等文件所在设备的分区,即grub的“根”;

    kernel /PATH/TO/VMLINUZ-FILE [PARAMETERS]:指明启动的内核;

    initrd /PATH/TO/INITRAMFS-FILE:指明内核匹配的ramdisk文件;

    password [--md5] STRING:启动选定的内核或操作系统时进行认证;


演示:

添加系统启动时的菜单项,首先编辑/etc/grub/grub.conf文件,如图:

grub应用


保存退出,并重新启动:

[[email protected] ~]# reboot


可以看到如下界面:

grub应用


重新编辑菜单,为CentOS 6 (test)这一菜单项指向的内核添加启动认证:

[[email protected] ~]# grub-md5-crypt 
Password:         //输入设定的密码;
Retype password: 
$1$n.0tB/$AStqQTLYFdyC5GcI0BymR/        //得到单向加密后的密码字符串;
[[email protected] ~]#


复制加密得到的密码字符串,添加至/etc/grub/grub.conf文件中CentOS 6 (test)菜单项之下:

grub应用


重新启动至菜单界面,这时如果要选择CentOS 6 (test)这一菜单项来启动时就需要通过密码验证才能加载内核:

grub应用


同样可添加菜单编辑认证,在/etc/grub/grub.conf文件中作如下修改:

grub应用这里全局的菜单编辑认证加密的密码同样可以由grub-md5-crypt生成,此处直接使用内核认证的密码。


重新启动,进入菜单界面:

grub应用

这里只能先键入p键并输入密码才能编辑菜单。



实例一:

新加硬盘,为这块新添加的硬盘安装grub程序,制作成在用户空间能单独运行bash的系统,并把硬盘拆下来装至另外的物理主机。


首先添加一块20G的硬盘:

grub应用


启动主机,首先为这块硬盘分区:/dev/sdb1, /dev/sdb2, /dev/sdb3,其中/dev/sdb1作为/boot分区,/dev/sdb2作为swap分区,而/dev/sdb3作为/分区:

[[email protected] ~]# fdisk /dev/sdb


分区后强制让内核重新识别:

[[email protected] ~]# partx -a /dev/sdb
BLKPG: Device or resource busy
error adding partition 1
BLKPG: Device or resource busy
error adding partition 2
BLKPG: Device or resource busy
error adding partition 3
[[email protected] ~]# 
[[email protected] ~]# cat /proc/partitions    //查看内核分区信息;
major minor  #blocks  name

   8        0   41943040 sda
   8        1     512000 sda1
   8        2   41430016 sda2
   8       16   20971520 sdb
   8       17     112423 sdb1    //内核已识别;
   8       18    2104515 sdb2
   8       19    5253255 sdb3
 253        0   37330944 dm-0
 253        1    4096000 dm-1


对各分区进行格式化:

[[email protected] ~]# mke2fs -t ext4 /dev/sdb1
[[email protected] ~]# mke2fs -t ext4 /dev/sdb3
[[email protected] ~]# mkswap /dev/sdb2


根分区挂载至/mnt/sysroot/,/boot分区挂载至/mnt/sysroot/boot:

[[email protected] ~]# mkdir /mnt/sysroot
[[email protected] ~]# mkdir /mnt/sysroot/boot
[[email protected] ~]# mount /dev/sdb3 /mnt/sysroot/
[[email protected] ~]# mount /dev/sdb1 /mnt/sysroot/boot/


给/dev/sdb这块磁盘安装grub:

[[email protected] ~]# grub-install --root-directory=/mnt/sysroot /dev/sdb
//注意:这里--root-directory不能指为/mnt/sysroot/boot,因为它会自动到/mnt/sysroot目录下
查找boot目录;
Probing devices to guess BIOS drives. This may take a long time.
Installation finished. No error reported.
This is the contents of the device map /mnt/sysroot/boot/grub/device.map.
Check if this is correct or not. If any of the lines is incorrect,
fix it and re-run the script `grub-install'.

(fd0)	/dev/fd0
(hd0)	/dev/sda
(hd1)	/dev/sdb
[[email protected] ~]#


查看文件:

[[email protected] ~]# cd /mnt/sysroot/boot/
[[email protected] boot]# ls        //查看/mnt/sysroot/boot分区下的文件;
grub  lost+found        //缺少Kernel文件和ramdisk文件;
[[email protected] boot]# cd grub/
[[email protected] grub]# ls -1        //查看/mnt/sysroot/boot/grub目录下的文件;
device.map
e2fs_stage1_5
fat_stage1_5
ffs_stage1_5
iso9660_stage1_5
jfs_stage1_5
minix_stage1_5
reiserfs_stage1_5
stage1
stage2
ufs2_stage1_5
vstafs_stage1_5
xfs_stage1_5
//在grub目录下缺少grub的配置文件grub.conf;


为/mnt/sysroot/boot目录下添加Kernel及ramdisk文件(此处直接复制/dev/sda上的文件):

[[email protected] ~]# cp /boot/vmlinuz-2.6.32-642.el6.x86_64 /mnt/sysroot/boot/vmlinuz  
[[email protected] ~]# cp /boot/initramfs-2.6.32-642.el6.x86_64.img /mnt/sysroot/boot/init
ramfs.img


创建grub的配置文件(/mnt/sysroot/boot/grub/grub.conf),并设定各属性:

default=0    //设定启动第一项;
timeout=5    //设定等待用户选择菜单的时长为5秒;
title CentOS 6 (Express)    //设定菜单标题;
    root (hd0,0)    //原本是(hd1,0),但拆下来装至另一空物理机上则为(hd0,0);
    kernel /vmlinuz ro root=/dev/sda3 init=/bin/bash
    //这里也一样,在本机上是/dev/sdb3,但装至另一空物理主机上则转为/dev/sda3;
    //此外,这里设定内核启动用户空间的第一个应用程序是/bin/bash,而不是原来的upstart;
    initrd /initramfs.img


根据FHS在根目录下创建各个基础目录:

[[email protected] ~]# cd /mnt/sysroot/
[[email protected] sysroot]# mkdir etc bin sbin usr var dev mnt media proc sys root lib 
lib64 home


拷贝/bin/bash程序及其所依赖到的库文件:

[[email protected] ~]# cp /bin/bash /mnt/sysroot/bin/
[[email protected] ~]# 
[[email protected] ~]# ldd /bin/bash     //查看bash程序所依赖到的库文件;
	linux-vdso.so.1 =>  (0x00007ffdd21fa000)    //库文件的访问入口;
	libtinfo.so.5 => /lib64/libtinfo.so.5 (0x0000003f02200000)
	libdl.so.2 => /lib64/libdl.so.2 (0x0000003efda00000)
	libc.so.6 => /lib64/libc.so.6 (0x0000003efde00000)
	/lib64/ld-linux-x86-64.so.2 (0x0000003efd600000)
[[email protected] ~]# 
[[email protected] ~]# cp /lib64/libtinfo.so.5 /mnt/sysroot/lib64/
[[email protected] ~]# cp /lib64/libdl.so.2 /mnt/sysroot/lib64/
[[email protected] ~]# cp /lib64/libc.so.6 /mnt/sysroot/lib64/
[[email protected] ~]# cp /lib64/ld-linux-x86-64.so.2 /mnt/sysroot/lib64/

以同样的方法,拷贝/bin/ls程序及其所依赖到的库文件。


所有步骤完成之后,先执行根切换测试一下:

[[email protected] ~]# chroot /mnt/sysroot/
bash-4.1# 
bash-4.1# ls -1                                                                        
bin
boot
dev
etc
home
lib
lib64
lost+found
media
mnt
proc
root
sbin
sys
usr
var
bash-4.1#
bash-4.1# exit     //由bash内嵌命令exit退出;
exit
[[email protected] ~]# 
[[email protected] ~]# sync    //执行同步;

测试成功!

关机之后,查看刚才添加的磁盘文件名 ==> CentOS 6.8-1.vmdk:

grub应用


新建虚拟机:

grub应用



创建时选择“使用现有虚拟磁盘”:

grub应用


选择刚才查看到的磁盘文件(CentOS 6.8-1.vmdk):

grub应用


创建完成之后启动新建的虚拟机,可以看到菜单界面如下:

grub应用


因为默认会开启selinux,而这里系统没有selinux的相关程序及配置文件,因此必须编辑菜单,关闭内核的selinux功能:

grub应用


编辑完之后,点击'b'键启动:

grub应用


启动之后顺利取得bash,并且可以运行ls程序:

grub应用

实验到此结束。



实例二:

破坏本机grub stage1,在不重启的情况下修复号grub。


用dd命令破坏grub:

[[email protected] ~]# dd if=/dev/sda of=/tmp/mbr.bak count=1 bs=512    //先备份MBR;
1+0 records in
1+0 records out
512 bytes (512 B) copied, 0.000376481 s, 1.4 MB/s
[[email protected] ~]# 
[[email protected] ~]# dd if=/dev/zero of=/dev/sda count=1 bs=100    //开始执行破坏操作;
1+0 records in
1+0 records out
100 bytes (100 B) copied, 0.0010733 s, 93.2 kB/s
//注意:破坏其前446Bytes即可。


修复grub:

方式一:
[[email protected] ~]# grub-install --root-directory=/ /dev/sda

方式二:
[[email protected] ~]# dd if=/dev/zero of=/dev/sda count=1 bs=100
1+0 records in
1+0 records out
100 bytes (100 B) copied, 0.000691542 s, 145 kB/s
[[email protected] ~]# grub
.....(中间省略).....
grub> 
grub> root (hd0,0)    //查看设备;
root (hd0,0)
 Filesystem type is ext2fs, partition type 0x83
grub> 
grub> setup (hd0)     //安装grub;
setup (hd0)
 Checking if "/boot/grub/stage1" exists... no
 Checking if "/grub/stage1" exists... yes
 Checking if "/grub/stage2" exists... yes
 Checking if "/grub/e2fs_stage1_5" exists... yes
 //这里只安装对应文件系统的stage1.5文件(当前为ext4文件系统);
 Running "embed /grub/e2fs_stage1_5 (hd0)"...  27 sectors are embedded.
succeeded
 Running "install /grub/stage1 (hd0) (hd0)1+27 p (hd0,0)/grub/stage2 /grub/grub.conf"... succeeded
Done.
grub> 
grub> quit
quit
[[email protected] ~]#

//注意:在grub命令行之下执行setup安装时,它会自动去读取指定设备的/boot/grub目录下的stage1
、stage1.5以及stage2文件,如果被破坏或者不存在则重新安装。



实例三:

破坏本机grub stage1,而后在救援模式下修复之。


破坏grub:

[[email protected] ~]# dd if=/dev/zero of=/dev/sda count=1 bs=100
1+0 records in
1+0 records out
100 bytes (100 B) copied, 0.000432857 s, 231 kB/s


重新启动,因为硬盘上的grub程序被破坏,这时第一个被扫描到有bootloader的是光盘,所以直接进入光盘,看到如下界面,这里选择进入救援模式:

grub应用


系统提示会将原有的根文件系统挂载至光盘系统的/mnt/sysp_w_picpath目录下,选择"Continue"。

grub应用

grub应用

grub应用


点击"OK"进入光盘的shell:

grub应用


执行根切换,并安装grub,而后重新启动:

grub应用

这样就可以正常启动系统了。