近日,不少社区用户反馈在系统升级后遇到系统无法启动的问题。为帮助大家快速定位和解决问题,同时加深对 Linux 系统的理解,我们整理归纳了此类问题的成因与分析步骤

特别感谢社区用户 @朕赐你炸鸡 的投稿支持!本文将以 deepin 25 Alpha 系统为例,为大家讲解 Linux 无法启动问题的排查思路,随着后续磐石系统的更新,部分步骤可能会随之变化(如 initrd 中对于 ostree 参数的处理,需要对 initrd 的相关脚本有一定的了解)。

注:以下方法同样适用于其他 Linux 发行版,细节可能会有不同。欢迎大家在 deepin 论坛或评论中沟通交流类似问题的分析过程和解决方案。

 

现象一:启动时出现 "grub>" 命令行,无法进入系统要如何处理?

如果引导文件(如 /boot/grub/grub.cfg文件)被损坏或删除,系统将无法正常启动。启动系统时可能出现下图中的场景:

针对这一场景,可以按照如下步骤解决:

在 GRUB 的终端交互界面中,使用 ls 命令查看当前块设备信息,并找到用户存放 boot 数据的分区位置(由于内核和 initrd 均位于 boot 目录下,后续步骤中需要用到该分区信息)。

例如,若 boot 数据位于 (hd0,gpt3),则通过输入 set root=(hd0,gpt3) 命令来指定该设备。

接下来,需要获取一份与当前系统版本相同的 grub.cfg 配置文件,并参考其内容,依次执行其中的 GRUB 命令。通常情况下,这些命令主要包括加载内核和 initrd

以下是其他机器的 /boot/grub/grub.cfg文件内容,供参考:

0.4.png

这里需要重点关注 Linux 和 initrd 这两行内容。Linux 命令用于指定引导的内核及其参数,不同机器的内核参数会有所不同。其中,root 和 ostree 参数尤为重要(这里针对的是 UOS 磐石镜像,其他发行版一般只需指定 root 参数即可)。

在UOS磐石镜像中,ostree参数指向系统部署的一个特定目录,通常位于 /persistent/ostree/data/[commit]/checkout。你可以先通过 ls 命令查看当前系统中实际的目录路径。

接下来,根据示例 grub.cfg的内容,使用Linux 命令指定 root=/dev/sda3和 ostree=...,注意在 ostree参数中需去掉实际目录前的 /persistent路径前缀。然后,通过 initrd命令加载实际的 initrd文件。完成后,使用 boot 命令开始加载当前设置的引导信息。

0.5.png

如果输入无误,此时即可进入系统。但当前系统的 /boot/grub/grub.cfg 仍处于损坏状态。

进入系统后,你可以通过运行 grub-mkconfig -o /boot/grub/grub.cfg 或 update-grub 命令来重建引导信息。之后,系统再次启动时,便无需再手动执行上述命令了。

0.6.png

至此,引导修复结束。

 

现象二:在 grub 引导项点击确认后,系统一直处于光标闪动的状态,如何处理?

现象示例如下图所示:

1.png

以上情况能是由于 /boot 目录下的 initrd 文件未被正确找到或加载导致的。

首先应检查 GRUB 引导参数中 initrd 相关配置是否存在错误。在 GRUB 菜单中,按 e 键进入引导参数编辑页面,仔细查看以 initrd 开头的那一行内容,确认其路径和文件名是否准确无误。initrd 是一个临时的根文件系统,内核在启动后会将其挂载,它在内核启动的早期阶段提供必要的驱动程序和工具,以便内核能够顺利访问实际的根文件系统。

针对此类问题,可以在 GRUB 参数编辑页面中,在以 Linux 开头的那一行末尾添加 "debug" 参数。若内核因找不到 initrd 而触发 panic,其报错内容大致如下:

2.png

上图可以明显看到rootfs 无法挂载,这种情况,只需分析为何找不到 initrd 的原因即可。

 

现象三:系统启动后卡在LOGO界面无日志输出,如何获取详细信息?

若系统启动后卡在某个服务上,屏幕上只显示系统 Logo 闪烁,而看不到相关日志。这种情况下,可以删除 GRUB 参数中的 quiet splash,以便查看详细的启动日志。

3.png

如果希望进一步查看内核日志,可以将 loglevel 参数的值设置为最高级别(7,0 是最低级别)。在 GRUB 菜单中,按 e 键进入引导参数编辑页面,然后在 Linux 开头的行中添加 loglevel=7 参数,最后按 F10 键加载引导信息,即可看到更详细的日志信息。

4.png

 

现象四:系统启动后进入紧急模式,要如何调试?

4.1.png

若系统启动后进入了紧急模式,可以先按下 Ctrl + Alt + Del 组合键重启系统。
随后在 GRUB 引导菜单页面中,选择当前的引导项,然后按下 e 键进入 GRUB 编辑模式。在以 linux 开头的行的末尾,添加 init=/bin/bash 参数。
4.2.png
接下来,按下 F10 键以加载引导程序,使系统启动后直接进入 bash 环境,而不是执行默认的 /sbin/init 进程。这样可以为我们后续的调试工作提供便利。
4.3.png

进入此阶段后,我们需要深入分析导致系统进入紧急模式(Emergency Mode)的原因。实际上,紧急模式是由 systemd 提供的一个特殊服务。

在 systemd 的启动流程中,如果某些关键服务启动失败,系统会触发 emergency.target,进而执行 emergency.service,最终进入紧急模式。以下是可能导致系统进入紧急模式的服务故障情况:

4.4.png
接下来,我们将深入分析系统的运行日志,以查找是否有相关服务的报错信息。通过执行 journalctl 命令,可以查询 systemd 服务的运行历史日志。在日志中,我们能够定位到问题发生的具体时间点。从日志信息中可以发现,local-fs.target 的依赖条件未能满足,导致该目标未能成功完成初始化。显然,这会触发系统的紧急模式(Emergency Mode)。
4.5.png

在进一步分析系统运行日志时,我们注意到日志中显示的红色超时信息,特别是 dev-disk-by***.service 服务的超时问题。通过查询相关资料,我们了解到 dev-disk-by***.service 是由 udev 规则触发并由 systemd 动态生成的服务,主要用于管理设备挂载。

系统中的设备挂载配置大概分为两部分:一部分在 initrd 中,另一部分在 /etc/fstab 文件中。从日志中可以看到,由于某个块设备的 UUID 挂载失败,导致 local-fs.target 未能完成初始化,进而触发了紧急模式(Emergency Mode)。

为了进一步排查问题,可以查询系统中所有块设备的 UUID。以下是常用的命令:

  • lsblk -f:列出所有块设备及其文件系统信息,包括 UUID;
  • blkid:直接显示所有块设备的 UUID,包括文件系统类型和分区信息。
执行以下命令来查询所有块设备的 UUID:
lsblk -f
该命令将列出系统中所有块设备的 UUID,帮助我们确定挂载失败的具体设备。
4.6.png

在排查过程中,我们发现 /dev/vda1 的 UUID 与报错信息中显示的 UUID 仅相差一位。这引起了我们的怀疑,于是进一步检查 /etc/fstab 文件。果不其然,问题出在这里——/etc/fstab 中的 UUID 写错了。

实际上,这是为了展示分析步骤而手动修改的(故意引入了一个错误)。将 /etc/fstab 中多余的字符“a”删除后,重新启动系统,一切便恢复正常了。

4.7.png

删除 UUID 最前面的多余字符“a”,保存文件后重启系统,随后系统便能够正常启动了。

 

现象五:启动后找不到 init 进程,要如何处理?

如果开机后能看到 GRUB 引导项,但系统无法正常启动,一般来说问题不大。

首先选中对应的引导项,按 e 键进入引导参数编辑页面。

6.png

如果系统在确认引导项后卡死,或者进入 initramfs 的 shell 页面,需要具体分析问题。例如,当系统实际的 / 无法找到时,通常会在 initramfs 阶段进入 busybox 内置的 shell 环境。

7.png

在这种情况下,可以查看内核参数,确认是否存在参数错误。例如,在 GRUB 编辑页面中,ostree= 参数的值被我故意在最后添加一个字母,从而让系统启动出错,方便展开接下来的排错过程。

8.png

此时,需要确认对应的目录是否存在,需要注意的是,ostree 参数的值通常需要以 /root/persistent 为前缀。

9.png

该问题源于系统启动过程中检查的 checkouta目录不存在,而实际正确的目录名称应为 checkout,这导致系统启动失败。在排查类似问题时,建议在内核参数中添加 debug选项,以获取 initramfs 初始化阶段的详细调试信息。

10.png

之后按下 F10,加载引导信息,当前这里的 checkout 我仍然修改为 checkouta,方便我们复现问题并分析,不出意外,我们又来到了熟悉的页面:

11.png

在 initramfs 的 shell 中,可以通过查看 /run/initramfs/initramfs.debug 文件来分析日志。日志文件中包含了详细的启动过程信息,通过查看日志可以找到报错的地方。

12.png

这里都是熟悉的 bash 脚本,接下来就是找到报错的地方。如下图所示,可以明显看到是 initramfs 阶段找不到对应的 init 进程,所以就单独启动了一个 sh 终端,方便使用者自行分析。

13.png

系统无法启动的原因已经找到,接下来就是查看为何找不到 init进程,这需要我们找到最初开始报错的地方,异常如下图所示,知道了问题我们在下次启动时,手动调整对应的 grub参数即可解决。

14.png

 

特殊场景:启动时完全卡死,无日志、键鼠无响应,要如何处理?

此类场景极少见,一般只会出现在未经适配的硬件上,需要电脑有串口,并通过串口调试获取内核日志。由于此类问题过于少见,若您无法通过串口定位,可以前往 deepin 社区论坛 联系专业的内核研发人员陪同定位。

补充知识:
1、如何利用 initramfs 断点定位问题,并分阶段调试?
由于 initramfs 中存在许多天然断点,这些断点可以通过在 GRUB 参数中添加 break=[break_point] 来触发。break_point 的取值包括以下几种:
  • top:initramfs 启动过程的最开始阶段。
  • modules:加载必要的内核模块阶段。
  • premount:挂载根文件系统之前的准备阶段。
  • mount:挂载根文件系统的阶段。
  • mountroot:挂载根文件系统的具体实现阶段。
  • bottom:initramfs 启动过程的最后阶段。

所以我们可以在特定阶段暂停启动过程,进而方便检查和干预启动过程,按照先后执行顺序如下所示:

  • top
    • 作用:这是 initramfs 启动过程的最开始阶段。
    • 调试用途:如果在启动参数中设置 break=top,系统会在 initramfs 的初始化脚本开始执行时暂停,方便用户检查早期启动环境。
  • modules
    • 作用:在这个阶段,系统会加载必要的内核模块。这些模块通常是根据 /conf/modules 文件的配置加载的,尽管在现代系统中,/conf/modules 文件可能不存在,因为很多模块已经通过 udev 动态加载。
    • 调试用途:如果设置 break=modules,系统会在加载模块之前暂停,用户可以检查模块加载情况或干预模块加载过程。
  • premount
    • 作用:这个阶段是为了挂载根文件系统做准备。它会运行 /scripts/init-premount 目录下的脚本,这些脚本通常用于初始化设备和文件系统,例如启动 udev 守护进程、处理 USB 或 FireWire 设备等。
    • 调试用途:如果设置 break=premount,系统会在挂载根文件系统之前暂停,用户可以检查设备初始化情况。
  • mount
    • 作用:这个阶段的主要任务是挂载根文件系统。它会执行 /scripts/local 中的 mountroot 函数,检测根文件系统的类型并将其挂载到 ${rootmnt}。
    • 调试用途:如果设置 break=mount,系统会在挂载根文件系统之前暂停,用户可以检查挂载参数或干预挂载过程。
  • mountroot
    • 作用:这个阶段是挂载根文件系统的具体实现阶段。它会根据启动参数(如 root=)解析根文件系统的设备路径,并尝试将其挂载到指定的挂载点。
    • 调试用途:如果设置 break=mountroot,系统会在挂载根文件系统的过程中暂停,用户可以检查挂载失败的原因。
  • bottom
    • 作用:这是 initramfs 启动过程的最后阶段。在这个阶段,系统会执行 **/scripts/init-bottom 目录下的脚本,这些脚本通常用于清理和完成启动过程中的最后一步操作。
    • 调试用途:如果设置 break=bottom,系统会在启动过程的最后阶段暂停,用户可以检查启动过程中的最后一步操作。

通过在 grub 中添加 break=[break_point] 可以让启动过程分别停止在不同的阶段并启动一个 sh 环境,更方便我们确认是哪个环节出现了问题。

在这个 sh 环境中,当我们检查完系统信息后,可以执行 exit 命令,此时系统会继续启动。

2、initrd 内容异常,应如何排查和修复?

当安装内核获驱动时,我们一般会执行 update-initramfs 命令来生成新的 initrd,偶尔可能会出现生成的 initrd 内容异常的情况。

由于 initrd 存放于 /boot/initrd.img-*,大小一般在200MB以内,所以在出现内容异常的情况时,我们可以通过 lsinitramfs 命令查看其内容,通过对比更新前后的 initrd 可以排查绝大部分问题。

15.png

通过排查,如果真的是再 update-initramfs 命令执行后 initrd 内容出现了问题,也不要恐慌,update-initramfs 也只是一个普通的 bash 脚本而已,分析其源码实现即可,这也是我们了解系统启动过程的一个绝好机会。

16.png

以上便是部分 Linux 启动故障的常见场景及解决方法,希望能为大家提供有效参考。

由于设备型号、硬件配置、操作习惯等差异,部分用户可能会遇到个例场景问题,如果以上方案无法解决您遇到的问题,欢迎大家前往deepin 社区论坛发帖提问,会有社区产品研发和热心用户第一时间响应。

 

 

发表评论