网站移动适配,下面哪些不是网页制作工具,ie域名,网络营销策略和营销策略的区别竞赛时对操作系统启动过程产生了些疑问#xff0c;于是问题导向地浅浅探究了下GRUB和initramfs相关机制#xff0c;相关笔记先放在这里了。
内核启动流程
在传统的BIOS系统中#xff0c;计算机具体的启动流程如下#xff1a;
电源启动#xff1a;当计算机的电源打开时于是问题导向地浅浅探究了下GRUB和initramfs相关机制相关笔记先放在这里了。
内核启动流程
在传统的BIOS系统中计算机具体的启动流程如下
电源启动当计算机的电源打开时电源供电给计算机的硬件设备。BIOS自检计算机的BIOS固件会自检硬件设备包括RAM、处理器、硬盘等以确保它们正常工作。引导设备选择BIOS会根据预先定义的启动顺序通常是硬盘、光驱、USB等选择一个启动设备。MBRMaster Boot Record加载如果选择的启动设备是硬盘BIOS会加载该硬盘的MBR其中包含了引导加载程序。GRUB加载MBR中的引导加载程序通常是GRUB或其他引导加载程序。GRUB会被加载到计算机的内存中并开始执行。GRUB菜单GRUB会显示一个菜单列出可供选择的操作系统或内核。操作系统加载用户选择操作系统后GRUB会加载相应的操作系统或内核并将控制权交给它。
在本次内核编译配置过程中最主要探究的是文件系统的装载过程也即介于6-7之间的部分。
概述
文件系统在启动流程中的发展历程可以分为以下三个部分 GRUB文件系统 由 GRUB 自身通过 BIOS 提供的服务加载 initramfs 由GRUB加载用于挂载真正的文件系统 真正的根文件系统
下面将介绍1和2两个流程。
GRUB GRUBGNU GRand Unified Bootloader是一种常用的引导加载程序用于在计算机启动时加载操作系统。 GRUB的主要功能是在计算机启动时提供一个菜单让用户选择要启动的操作系统或内核。它支持多个操作系统包括各种版本的Linux、Windows、BSD等。通过GRUB用户可以在多个操作系统之间轻松切换。 除了操作系统选择GRUB还提供了一些高级功能例如引导参数的设置、内存检测、系统恢复等。它还支持在启动过程中加载内核模块和初始化RAM磁盘映像initrd或initramfs。 GRUB具有高度可配置性允许用户自定义引导菜单、设置默认启动项、编辑内核参数等。它还支持引导加载程序间的链式引导可以引导其他引导加载程序如Windows的NTLDR。 GRUB的基本作用流程为
BIOS加载MBRMBR加载GRUB开始执行GRUB程序GRUB程序会读取grub.cfg配置文件GRUB程序依据配置文件进行内核的加载、根文件系统的挂载等操作最后将主导权转交给内核
grub.cfg
内核启动时GRUB程序会读取/boot/grub/目录下的GRUB配置文件grub.cfg其中记录了所有GRUB菜单可供选择的内核选项menuentry及其对应的启动依赖参数。以6.4.0内核选项为例
# menuentry标识着GRUB菜单中的一个内核选项
menuentry Ubuntu --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option gnulinux-simple-XXX {recordfail # 记录上次启动是否失败用于处理启动失败的情况load_video # 加载视频驱动模块用于在启动过程中显示图形界面gfxmode $linux_gfx_mode # 设置图形模式insmod gzio # 加载gzio模块提供对GZIP压缩和解压缩功能的支持# 如果是在Xen虚拟化平台上则加载xzio和lzopio模块if [ x$grub_platform xxen ]; then insmod xzio; insmod lzopio; fi insmod part_gpt # 加载part_gpt模块支持GUID分区表GPTinsmod ext2 # 加载ext2模块支持ext2文件系统# 设置文件系统的根分区set roothd0,gpt3 if [ x$feature_platform_search_hint xy ]; thensearch --no-floppy --fs-uuid --setroot --hint-bioshd0,gpt3 --hint-efihd0,gpt3 --hint-baremetalahci0,gpt3 XXXelsesearch --no-floppy --fs-uuid --setroot XXXfilinux /boot/vmlinuz-6.4.0-rc3 rootUUIDXXX ro text # 指定内核映像的路径和启动参数initrd /boot/initrd.img-6.4.0-rc3 # 指定initramfs映像的路径
}可以看到grub.cfg主要记录了一些该内核启动需要的依赖module以及内核映像和initramfs映像的路径。
menuentry的代码中有以下几个要点值得注意 insmod gzio 由于加载gzio模块提供对GZIP压缩和解压缩功能的支持。 看到这里我第一反应是觉得有点割裂为啥这看着比较无关紧要的解压缩功能要在内核启动之前就需要有呢于是我想起来在配置内核时有一个选项是这样的 在配置选项中我们选择了对initramfs的支持并且勾选了Support initial ramdisk/ramfs compressed using gzip 也即在编译时通过gzip压缩initramfs的大小以节省空间。 所以说我们在内核启动之前持有的initramfs处于被压缩的状态。故而我们自然需要在内核启动之前安装gzio模块从而支持之后对initramfs的解压缩了。 insmod ext2 这句代码说明GRUB的临时文件系统为ext2类型这句代码事实上是在安装GRUB建立临时文件的必要依赖包从而GRUB程序之后才能建立其临时文件系统、从/boot/initrd.img获取initramfs映像。 linux /boot/vmlinuz-6.4.0-rc3 rootUUIDXXX ro text 指定了启动参数也即将根文件系统以只读ro的方式挂载在rootUUIDXXX对应的块设备上并且默认以text方式也即非图形化的Shell界面启动内核。 此处的启动参数可在下一个部分介绍的grub文件中个性化。
grub.cfg的生成与修改
实际运用中很多时候需要对启动参数进行一些修改。下面介绍两种修改grub.cfg的方法。
/etc/default/grub
可以看到grub.cfg其实格式较为固定也即由一系列内容也比较相似的menuentry构成。因而实际上我们是通过grub.d生成grub.cfg的6.S081实验中事实上也涉及了这一点而/etc/default/grub则是GRUB程序以及grub.cfg生成的配置文件。下面介绍下该文件主要有哪些配置选项。
# If you change this file, run update-grub afterwards to update
# /boot/grub/grub.cfg.
# For full documentation of the options in this file, see:
# info -f grub -n Simple configuration# 开机时GRUB界面的持续时间此处设置为30s
GRUB_TIMEOUT30
GRUB_CMDLINE_LINUX# 不使用图形化界面
#GRUB_TERMINALconsole
# 图形化界面的大小
#GRUB_GFXMODE640x480
# 不使用UUID
#GRUB_DISABLE_LINUX_UUIDtrue# 隐藏recovery mode
#GRUB_DISABLE_RECOVERYtrue重点看下这几个参数 GRUB_CMDLINE_LINUX 表示最终生成的grub.cfg中的每一个menuentry中的linux那一行需要附加什么参数。 例如说如果设置为 # 表示initramfs在挂载真正的根文件系统之前需要等待120s用于防止磁盘没准备好导致的挂载失败
GRUB_CMDLINE_LINUXrootdelay120 那么最终在menuentry中的启动参数就为 linux /boot/vmlinuz-6.4.0-rc3 rootUUIDXXX ro rootdelay120 text其他一些常见的选项 # 直接以路径来标识块设备而非使用UUID。此为old option建议尽量使用UUID
GRUB_CMDLINE_LINUXroot/dev/sda3
# 标明init进程启动后第一个进程的具体路径。此处指明为/bin/sh
GRUB_CMDLINE_LINUXinit/bin/shGRUB_DEFAULT 参考 可以用来指定重启时的内核选项。如GRUB_DEFAULT1 0表示选择第一个菜单界面的第2栏Advanced for Ubuntu和第二个菜单的第1个内核。
在修改完grub文件之后我们需要执行sudo update-grub来重新生成grub.cfg文件供下次启动使用。
在GRUB界面直接修改 我们可以在GRUB界面选中所需内核按下e键 然后就可以对启动参数进行修改^X退出。
值得注意的是此修改仅对本次启动有效。如果需要长期修改建议还是通过第一种方法去修改。
initramfs
GRUB程序会通过initrd.img启动initramfs从而进行真正的根文件系统挂载。 initrd.img是一个Linux系统中的初始化内存盘initial RAM disk的映像文件。它是一个压缩的文件系统映像通常在引导过程中加载到内存中并提供了一种临时的根文件系统以便在正式的根文件系统通常位于硬盘上可用之前提供必要的功能和模块。 我们可以通过unmkinitramfs /boot/initrd.img-6.4.0-rc3 /tmp/initrd/命令解压initrd探究里面到底有什么玩意。
├── bin - usr/bin
├── conf
├── etc
├── init
├── lib - usr/lib
├── lib32 - usr/lib32
├── lib64 - usr/lib64
├── libx32 - usr/libx32
├── run
├── sbin - usr/sbin
├── scripts
├── usr
└── var
init可以看到这实际上就是一个小型的文件系统也即initramfs。它有自己的built-in ShellBusyBox 有一些较少的Shell命令bin和sbin目录下以及用来挂载真正的根文件系统的代码逻辑存储在scripts目录下。【我猜】在正常情况下系统会执行scripts下的脚本代码挂载真正的文件系统。当挂载出现异常时系统就会将控制权交给initramfs内置的Shell BusyBox由用户自己探究出了什么问题。
我们接下来可以追踪下initramfs的script目录下的文件系统挂载流程。
挂载真正文件系统的主要函数为local_mount_root
# 仅展示主要流程代码
local_mount_root()
{# 预处理获取参数等也即上面grub.cfg配置的rootUUIDlocal_topif [ -z ${ROOT} ]; thenpanic No root device specified. Boot arguments must include a root parameter.fi# 根据UUID获取对应的块设备local_device_setup ${ROOT} root file systemROOT${DEV}# 挂载前的预处理local_premount# 挂载mount ${roflag} ${FSTYPE:-t ${FSTYPE}} ${ROOTFLAGS} ${ROOT} ${rootmnt?}
}由于研究这个是错误驱动乐因而我只主要看了下local_device_setup
# $1device ID to mount设备ID
# $2optionname (for root and etc)要挂载的是什么玩意此处应为root file system
# $3panic if device is missing (true or false, default: true)
# Sets $DEV to the resolved device node $DEV是最终获取到的块设备
local_device_setup()
{local dev_id$1local name$2local may_panic${3:-true}local real_devlocal time_elapsedlocal count# 获取grub.cfg的rootdelay参数的设备等待时间。如果没有该参数默认是30秒local slumber30if [ ${ROOTDELAY:-0} -gt $slumber ]; thenslumber$ROOTDELAYfi# 等待设备case $dev_id inUUID*|LABEL*|PARTUUID*|/dev/*)FSTYPE$( wait-for-root $dev_id $slumber );;*)wait_for_udev 10;;esac# 等待结束了。如果条件为真说明还是获取不到对应的设备那就只能说明这个设备死了# 所以我们就得把问题告诉用户让用户自己解决并且进入BusyBox Shell# Weve given up, but well let the user fix matters if they canwhile ! real_dev$(resolve_device ${dev_id}) ||! get_fstype ${real_dev} /dev/null; doif ! $may_panic; thenecho Gave up waiting for ${name}return 1fiecho Gave up waiting for ${name} device. Common problems:echo - Boot args (cat /proc/cmdline)echo - Check rootdelay (did the system wait long enough?)if [ ${name} root ]; thenecho - Check root (did the system wait for the right device?)fiecho - Missing modules (cat /proc/modules; ls /dev)panic ALERT! ${dev_id} does not exist. Dropping to a shell!doneDEV${real_dev}
}可以看到这里如果进入错误状态最终就是这样的效果2333