Linux操作系统内核被Bootloader加载进内存完成初始化后,会经历两个用户空间启动阶段。一是操作系统内核会解压Initramfs到内存,并将其挂载为临时的根文件系统。Initramfs的目标是挂载并切换到系统启动后正常使用的根文件系统。如果根文件系统所在设备无法找到或者无法挂载,操作系统启动失败并且会进入Emergency Shell,提供给用户诊断启动失败的原因;二是切换到真实根文件系统后,启动并初始化相关的应用服务。该阶段一般不会影响操作系统的启动,但是如果有应用服务启动失败,会直接影响相关的业务运行。本文所要探索的即为用户空间启动的第一阶段。Linux操作系统内核挂载完成Initramfs之后会启动第一个用户空间进程init进程。该进程是Initramfs中的Systemd1,如图1所示。
图 1. Initramfs init进程
Systemd进程会按照Systemd的service文件中定义的启动顺序(如图2、图3所示)启动用于查找与挂载真实根文件系统的服务、命令。这其中涉及一个重要的步骤为利用udev2从Initramfs加载相应设备的驱动,并在/dev目录下创建相应的设备文件。根据启动顺序图中可以得到,经过一系列的Services最终到达switch-root,完成真实根文件系统的切换。这里仅列举几个重要的Systemd service功能阐述,如下表1所示。
Service名称
Service作用
dracut-cmdline.service
获取kernel的启动参数,主要解析root, type。
systemd-udevd.service
启动udev守护进程。
system-udev-trigger.service
运行udevadm trigger触发内核重发uevent。
dracut-mount.service
mount根文件系统到/sysroot。
switch-root
切换/sysroot为系统根文件系统。
图2. 启动顺序-1
图3. 启动顺序-2
整个用户空间启动过程中,可以通过修改系统启动参数“rd.break3”,让系统启动停止于某个阶段,并进入Emergency Shell。不同阶段的当前系统所运行的服务,配置文件、系统的状态都存在着差别。本文在下一节讲述实际案例时就会使用这个技巧来窥探系统的详细状态。
通过下图展示该操作系统启动失败案例的现象,终端反复打印着启动失败的错误“dracut-initqueue timeout”,并且提示相应的LVM逻辑卷”/dev/ctyunos/*”不存在。因此
图 4. 系统启动失败
我们利用系统启动参数”rd.break=initqueue”,让系统启动停止于dracut-initqueue阶段。首先查询并使能系统当前的vggroup,然后对该vggroup的可用性通过mount进行检测,发现当前的vgroup没有任何问题。根据错误信息现在把问题焦点放到LVM逻辑卷
图 5. vggroup可用性检查
不存在的错误提示。通过查看系统启动时的参数,尤其是根文件系统的路径参数”root=/dev/mapper/ctyunos-root”,与图5所示内容比较发现vggroup的名字不一致。系统上的vggroup名称为“rootvg”,而且系统启动参数却是“ctyunos”,这便是该案例中系统启动失败的原因。系统启动参数如图6所示。
图 6. 系统启动参数vggroup
修复本案例的系统启动失败问题需要在Grub启动菜单中修改启动参数,修改前的启动参数与修改后的启动参数分别如图7、图8所示。当前的修改只是为了本次能够正常的启动系统,如果系统重新启动,同样的失败还会再次发生。因此为了让系统后续启
图 7. Grub启动菜单错误参数
图 8. Grub启动菜单正确参数
动都能正常启动,需要在文件系统上将Grub配置文件/etc/default/grub中的相关启动参数修改为正确值,并执行命令grub2-mkconfig在/boot分区生成正确的grub.cfg文件。