linux启动流程(二):grub2

admin 2022年09月01日 1,652次浏览

1、boot loader的两个stage

在 BIOS 读取完信息后,接下来就是会到第一个引导设备的 MBR 去读取 boot loader。boot loader 具有菜单功能、直接加载内核文件以及控制权移交的功能等, 系统必须要有 loader 才有办法加载该操作系统的内核,但是 MBR 的大小只有 446 bytes ,是无法安装下这个 loader 的,为此,Linux 将 boot loader 的程序代码执行与设置值加载分成两个阶段 (stage) 来执行

  • Stage 1:

    第一阶段为执行 boot loader 的主程序,这个主程序必须要被安装在引导区,也就是是 MBR 或者启动扇区。但因为 MBR 空间有限,所以,MBR 或启动扇区通常仅安装 boot loader 的最小主程序, 并没有安装 loader 的相关配置文件

  • Stage 2:

    第二阶段通过 boot loader 加载所有配置文件与相关的环境参数文件(包括文件系统定义与主要配置文件 grub.cfg),通常配置文件都在 /boot 目录下

2、grub2配置文件

grub2 有众多的优点:

  • 识别与支持较多的文件系统,并且可以使用 grub2 的主程序直接在文件系统中搜寻内核文件

  • 开机的时候,可以编辑与修改启动设置项

  • 可以动态搜寻配置文件,而不需要在修改配置文件后重新安装 grub2

2.1、磁盘与分区在grub2中的代号

安装在 MBR 的 grub2 主程序,最重要的任务之一就是从磁盘当中加载内核文件,从而让核心能够顺利的驱动整个系统的硬件。所以,grub2 必须要识别硬盘才行。

grub2 对硬盘的代号设置与传统的 Linux 磁盘代号是不同的,grub2 对硬盘的识别使用的是如下的代号:

  • (hd0,1) :默认语法,由 grub2 自动判断分区格式
  • (hd0,msdos1) :此磁盘的分区为传统的 MBR 模式
  • (hd0,gpt1) :此磁盘的分区为 GPT 模式

2.2、grub2配置文件维护

因为 grub.cfg 内容太复杂,数据量非常大,且 grub2 官方不建议手动修改,而是应该要通过 /etc/default/grub 这个主要环境配置文件与 /etc/grub.d/ 目录内的相关配置文件来处理比较安全

2.2.1、/etc/default/grub 主要环境配置文件

[root@localhost ~]# cat /etc/default/grub
# 指定默认的倒数秒数
GRUB_TIMEOUT=5
# 默认启动项
GRUB_DEFAULT=saved
# 是否要隐藏次选项
GRUB_DISABLE_SUBMENU=true
# 指定数据输出终端格式,默认为命令行
GRUB_TERMINAL_OUTPUT="console"
# 就是在menuentry括号内的linux16后接的内核参数
GRUB_CMDLINE_LINUX="crashkernel=auto rd.lvm.lv=centos/root rd.lvm.lv=centos/swap rhgb quiet"
# 取消恢复选项的操作
GRUB_DISABLE_RECOVERY="true"
  • GRUB_TIMEOUT

    倒数时间设置,后面接倒数的秒数即可;如果不想等待则输入 0;如果一定要选择,则填 -1 即可

  • GRUB_TIMEOUT_STYLE

    是否隐藏选择项,这个选项可选择的设定值有 menu, countdown, hidden 等等。如果没有设定,预设是 menu 的意思。这个选项主要是设置要不要显示选项,countdown 会在屏幕上显示剩余的等待秒数,而 hidden 则什么也不显示

  • GRUB_DEFAULT

    默认的启动项,能使用的设定值包括:saved,、数字、title 名、ID 等

  • GRUB_CMDLINE_LINUX

    内核的外加参数功能,如果内核在启动的时候还需要加入额外的参数,就在这里添加。这个主要环境配置文件编写完毕之后,必须要使用 grub2-mkconfig 来重建 grub.cfg 才行

2.2.2、/etc/grub.d/*

选项创建的脚本,grub2-mkconfig 会去分析 /etc/grub.d/* 里面的文件,然后执行该文件来创建 grub.cfg 文件。通常 /etc/grub.d/ 目录下会包含以下文件:

  • 00_header:主要在建立初始的显示项目,包括需要加载的模块分析、屏幕终端的格式、倒数秒数、选项是否需要隐藏等,大部分在 /etc/default/grub 里面所设置的变量,大概都会在这个脚本当中被利用来重建 grub.cfg

  • 10_linux:根据分析 /boot 下的文件,尝试找到正确的 linux内核与读取这个内核需要的文件系统模块与参数等,都在这个脚本运行后找到并设置到 grub.cfg 当中。因为这个脚本会将所有在 /boot 下的每一个内核文件都对应到一个选项,因此内核文件数量越多,开机选项就越多

  • 30_os-prober:这个脚本默认会到系统上找其他的分区里面可能含有的操作系统,然后将该操作系统做成选项来处理,如果不想要让其他的操作系统被检测到并拿来启动,可以在 /etc/default/grub 里面加上 GRUB_DISABLE_OS_PROBER=true 取消这个文件的运行

  • 40_custom:如果还有其他想要自己手动加上去的选项,或者是其他的需求,那么就在这里添加

  • menuentry 启动项的配置

    一个 menuentry 就是一个选项,常见的有下面几个设置:

    • 直接指定内核启动

      如果是 Linux 的内核要直接被用来启动,那么可以通过 grub2-mkconfig 去抓10_linux 这个脚本直接制作即可。如果需要配置特殊的参数,可以这样操作:先到 grub.cfg 当中取得你要制作的那个内核的选项,然后将它复制到 40_custom 当中,再到 40_custom 当中根据需求修改即可,最后执行 grub2-mkconfig -o /boot/grub2/grub.cfg 重建 grub.cfg 即可

    • 通过 chainloader 的方式移交启动控制权

      所谓的 chain loader(开机管理程序的链接)仅是在将控制权交给下一个 boot loader 而已,所以 grub2 并不需要识别与找出内核的文件名 ,而只是将 boot 的控制权交给下一个启动扇区或 MBR 内的 boot loader 而已,所以通常它也不需要去查验下一个 boot loader 的文件系统。通常 chain loader 的设置只要两个就够了,一个是预计要前往的 boot sector 所在的分区代号, 另一个则是设置 chainloader 在那个分区的启动扇区上

      示例: Windows 分区在 /dev/sda1 ,且只有一块硬盘,那么要 grub 将控制权交给 windows 的 loader,就需要这样配置:

      menuentry "Windows" {
       # 加载chainloader的模块
       insmod chain
       # 加入windows所在的文件系统模块
       insmod ntfs
       # 设置启动分区
       set root=(hd0,1)
       # 从启动扇区读取loader
       chainloader +1
      }
      

3、initramfs

3.1、initramfs简介

initramfs 的目的在于提供启动过程中所需要的最重要内核模块,以让系统启动过程可以顺利完成。需要 initramfs 的原因,是因为内核模块放置于 /lib/modules/$(uname -r)/kernel/ 当中, 这些模块必须要根目录被挂载时才能够被读取。但是如果内核本身不具备磁盘的驱动程序时, 就无法挂载根目录,也就没有办法获取到驱动程序,造成无法启动

initramfs 可以将 /lib/modules/ 内的启动过程中必需的模块打包到 initramfs 中,然后在启动时,通过主机的 INT 13 硬件功能将该文件读出来解压缩,并且 initramfs 在内存内会模拟成为根目录, 由于此虚拟文件系统主要包含磁盘与文件系统的模块,因此内核最后就能够识别实际的磁盘,然后就能够进行实际根目录的挂载,因此,initramfs 内所包含的模块大多是与启动过程有关,且主要以文件系统及硬盘模块 (如 usb, SCSI 等) 为主的

通常在以下情况下需要 initramfs:

  • 根目录所在磁盘为 SATA、USB 或 SCSI 等接口

  • 根目录所在文件系统为 LVM, RAID 等特殊格式

  • 根目录所在文件系统为非传统 Linux 识别的文件系统

  • 其他必须要在内核加载时提供的模块

3.2、构建initramfs

通常情况下不需要构建 initramfs 文件,但是有时需要对 initramfs 进行修改时,就需要重新构建 initramfs 了,在 centos7 中使用的工具为 dracut

  • 语法

    [root@localhost ~]# dracut [-fv] [--add-drivers 列表] initramfs 名称 内核版本
    

    选项与参数

    • -f:强制编译initramfs,如果initramfs文件已经存在,则覆盖掉旧文件
    • -v:显示dracut的运行过程
    • –add-drivers 列表:在原本的默认内核模块中,增加模块,模块位于内核所在目录 /lib/modules/$(uname -r)/kernel/*
    • initramfs 名称 :initramfs 的文件名,建议使用initramfs开头,后面接版本与功能
    • 内核版本 :默认是当前运行的内核版本,也可以输入不同的版本
    • –modules :将dracut所提供的启动所需模块 (内核模块) 加载,可用模块在 /usr/lib/dracut/modules.d/目录内
    • –gzip|–bzip2|–xz:压缩initramfs的方式,默认使用gzip
    • –filesystems :加入额外的文件系统支持
  • 示例

    在新的 initramfs 中加入 e1000e 网卡驱动与 ext4、nfs 文件系统

    # 构建initramfs
    [root@localhost ~]# dracut -v --add-drivers "e1000e" --filesystems "ext4 nfs" initramfs-new.img $(uname -r)
    
    # 查看initramfs中的内容
    [root@localhost ~]# lsinitrd initramfs-new.img | grep -E '(e1000|ext4|nfs)'