近日,不少社区用户反馈在系统升级后遇到系统无法启动的问题。为帮助大家快速定位和解决问题,同时加深对 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
文件内容,供参考:
这里需要重点关注 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
命令开始加载当前设置的引导信息。
如果输入无误,此时即可进入系统。但当前系统的 /boot/grub/grub.cfg 仍处于损坏状态。
进入系统后,你可以通过运行 grub-mkconfig -o /boot/grub/grub.cfg 或 update-grub 命令来重建引导信息。之后,系统再次启动时,便无需再手动执行上述命令了。
至此,引导修复结束。
现象二:在 grub 引导项点击确认后,系统一直处于光标闪动的状态,如何处理?
现象示例如下图所示:
以上情况能是由于 /boot
目录下的 initrd
文件未被正确找到或加载导致的。
首先应检查 GRUB 引导参数中 initrd
相关配置是否存在错误。在 GRUB 菜单中,按 e
键进入引导参数编辑页面,仔细查看以 initrd
开头的那一行内容,确认其路径和文件名是否准确无误。initrd
是一个临时的根文件系统,内核在启动后会将其挂载,它在内核启动的早期阶段提供必要的驱动程序和工具,以便内核能够顺利访问实际的根文件系统。
针对此类问题,可以在 GRUB 参数编辑页面中,在以 Linux
开头的那一行末尾添加 "debug"
参数。若内核因找不到 initrd
而触发 panic,其报错内容大致如下:
上图可以明显看到rootfs 无法挂载,这种情况,只需分析为何找不到 initrd 的原因即可。
现象三:系统启动后卡在LOGO界面无日志输出,如何获取详细信息?
若系统启动后卡在某个服务上,屏幕上只显示系统 Logo 闪烁,而看不到相关日志。这种情况下,可以删除 GRUB 参数中的 quiet splash
,以便查看详细的启动日志。
如果希望进一步查看内核日志,可以将 loglevel
参数的值设置为最高级别(7,0 是最低级别)。在 GRUB 菜单中,按 e
键进入引导参数编辑页面,然后在 Linux
开头的行中添加 loglevel=7
参数,最后按 F10
键加载引导信息,即可看到更详细的日志信息。
现象四:系统启动后进入紧急模式,要如何调试?
GRUB
引导菜单页面中,选择当前的引导项,然后按下 e
键进入 GRUB
编辑模式。在以 linux
开头的行的末尾,添加 init=/bin/bash
参数。
F10
键以加载引导程序,使系统启动后直接进入 bash
环境,而不是执行默认的 /sbin/init
进程。这样可以为我们后续的调试工作提供便利。
进入此阶段后,我们需要深入分析导致系统进入紧急模式(Emergency Mode)的原因。实际上,紧急模式是由 systemd
提供的一个特殊服务。
在 systemd
的启动流程中,如果某些关键服务启动失败,系统会触发 emergency.target
,进而执行 emergency.service
,最终进入紧急模式。以下是可能导致系统进入紧急模式的服务故障情况:

journalctl
命令,可以查询 systemd
服务的运行历史日志。在日志中,我们能够定位到问题发生的具体时间点。从日志信息中可以发现,local-fs.target
的依赖条件未能满足,导致该目标未能成功完成初始化。显然,这会触发系统的紧急模式(Emergency Mode)。
在进一步分析系统运行日志时,我们注意到日志中显示的红色超时信息,特别是 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,包括文件系统类型和分区信息。
lsblk -f

在排查过程中,我们发现 /dev/vda1
的 UUID 与报错信息中显示的 UUID 仅相差一位。这引起了我们的怀疑,于是进一步检查 /etc/fstab
文件。果不其然,问题出在这里——/etc/fstab
中的 UUID 写错了。
实际上,这是为了展示分析步骤而手动修改的(故意引入了一个错误)。将 /etc/fstab
中多余的字符“a”删除后,重新启动系统,一切便恢复正常了。
删除 UUID 最前面的多余字符“a”,保存文件后重启系统,随后系统便能够正常启动了。
现象五:启动后找不到 init 进程,要如何处理?
如果开机后能看到 GRUB 引导项,但系统无法正常启动,一般来说问题不大。
首先选中对应的引导项,按 e
键进入引导参数编辑页面。
如果系统在确认引导项后卡死,或者进入 initramfs
的 shell 页面,需要具体分析问题。例如,当系统实际的 /
无法找到时,通常会在 initramfs
阶段进入 busybox
内置的 shell 环境。
在这种情况下,可以查看内核参数,确认是否存在参数错误。例如,在 GRUB 编辑页面中,ostree=
参数的值被我故意在最后添加一个字母,从而让系统启动出错,方便展开接下来的排错过程。
此时,需要确认对应的目录是否存在,需要注意的是,ostree
参数的值通常需要以 /root/persistent
为前缀。
该问题源于系统启动过程中检查的 checkouta
目录不存在,而实际正确的目录名称应为 checkout
,这导致系统启动失败。在排查类似问题时,建议在内核参数中添加 debug
选项,以获取 initramfs 初始化阶段的详细调试信息。
之后按下 F10,
加载引导信息,当前这里的 checkout
我仍然修改为 checkouta,
方便我们复现问题并分析,不出意外,我们又来到了熟悉的页面:
在 initramfs 的 shell 中,可以通过查看 /run/initramfs/initramfs.debug
文件来分析日志。日志文件中包含了详细的启动过程信息,通过查看日志可以找到报错的地方。
这里都是熟悉的 bash 脚本,接下来就是找到报错的地方。如下图所示,可以明显看到是 initramfs 阶段找不到对应的 init
进程,所以就单独启动了一个 sh
终端,方便使用者自行分析。
系统无法启动的原因已经找到,接下来就是查看为何找不到 init
进程,这需要我们找到最初开始报错的地方,异常如下图所示,知道了问题我们在下次启动时,手动调整对应的 grub
参数即可解决。
特殊场景:启动时完全卡死,无日志、键鼠无响应,要如何处理?
此类场景极少见,一般只会出现在未经适配的硬件上,需要电脑有串口,并通过串口调试获取内核日志。由于此类问题过于少见,若您无法通过串口定位,可以前往 deepin 社区论坛 联系专业的内核研发人员陪同定位。
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 可以排查绝大部分问题。
通过排查,如果真的是再 update-initramfs
命令执行后 initrd 内容出现了问题,也不要恐慌,update-initramfs 也只是一个普通的 bash 脚本而已,分析其源码实现即可,这也是我们了解系统启动过程的一个绝好机会。
以上便是部分 Linux 启动故障的常见场景及解决方法,希望能为大家提供有效参考。
由于设备型号、硬件配置、操作习惯等差异,部分用户可能会遇到个例场景问题,如果以上方案无法解决您遇到的问题,欢迎大家前往deepin 社区论坛发帖提问,会有社区产品研发和热心用户第一时间响应。