注:主要以UOS举例,其他发行版步骤类似,只是细节可能有些不同。
引导文件丢失
如果用户的引导文件(如/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
命令查看当前系统中实际的目录路径(幸好可以补全,否则手动输入ostree
参数可能会让人感到头疼……-_ -)。
接下来,根据示例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
是一个临时的根文件系统,内核在启动后会将其挂载,它在内核启动的早期阶段提供必要的驱动程序和工具,以便内核能够顺利访问实际的根文件系统。
针对此类问abbrlink题,可以在 GRUB 参数编辑页面中,在以 linux
开头的那一行末尾添加 "debug"
参数。若内核因找不到 initrd
而触发 panic,其报错内容大致如下:`
上图可以明显看到rootfs 无法挂载,这种情况,只需分析为何找不到 initrd 的原因即可.
启动后卡住
有时候系统启动后会卡在某个服务上,屏幕上只显示系统 logo 闪烁,而看不到相关日志。这种情况下,可以删除 GRUB 参数中的 quiet splash
,以便查看详细的启动日志.
如果希望进一步查看内核日志,可以将 loglevel
参数的值设置为最高级别(7,0 是最低级别)。在 GRUB 菜单中,按 e
键进入引导参数编辑页面,然后在 linux
开头的行中添加 loglevel=7
参数,最后按 F10
键加载引导信息,即可看到更详细的日志信息。
启动后系统进入emergency mode
系统启动后进入了紧急模式怎么办
按下 Ctrl + Alt + Del 组合键重启系统。在 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,包括文件系统类型和分区信息。
执行以下命令来查询所有块设备的 UUID:
1 | blkid -f |
该命令将列出系统中所有块设备的 UUID,帮助我们确定挂载失败的具体设备
在排查过程中,我们发现 /dev/vda1
的 UUID 与报错信息中显示的 UUID 仅相差一位。这引起了我们的怀疑,于是进一步检查 /etc/fstab
文件。果不其然,问题出在这里——/etc/fstab
中的 UUID 写错了。
实际上,这是为了展示分析步骤而手动修改的(故意引入了一个错误)。将 /etc/fstab
中多余的字符“a”删除后,重新启动系统,一切便恢复正常了。
(注:实际是我手动改的,为了展示 /etc/fstab 内容错误导致系统无法正常启动时的分析步骤)
删除 UUID 最前面的多余字符“a”,保存文件后重启系统,随后系统便能够正常启动了。
找不到 init进程
如果开机后能看到 GRUB 引导项,但系统无法正常启动,通常问题不大。
选中对应的引导项,按 e
键进入引导参数编辑页面。
如果系统在确认引导项后卡死,或者进入 initramfs
的 shell 页面,需要具体分析问题。例如,当系统实际的 /
无法找到时,通常会在 initramfs
阶段进入 busybox
内置的 shell 环境。
在这种情况下,可以查看内核参数,确认是否存在参数错误。例如,在 GRUB 编辑页面中,ostree=
参数的值被我故意在最后添加一个字母,从而让系统启动出错,方面展开接下来的排错过程。
此时,需要确认对应的目录是否存在。注意,ostree
参数的值通常需要以 /root/persistent
为前缀,之所以如此,当然是因为我提前看过initrd中对 ostree 参数的处理啦。
该问题源于系统启动过程中检查的checkouta
目录不存在,而实际正确的目录名称应为checkout
,这导致系统启动失败。
在排查类似问题时,建议在内核参数中添加debug
选项,以获取 initramfs 初始化阶段的详细调试信息。
之后按下 F10
,加载引导信息,当前这里的checkout
我仍然修改为checkouta
,方便我们复现问题并分析,不出意外,我们又来到了熟悉的页面
在 initramfs 的 shell 中,可以通过查看 /run/initramfs/initramfs.debug
文件来分析日志。日志文件中包含了详细的启动过程信息,通过查看日志可以找到报错的地方。
这里都是熟悉的 bash 脚本,相信阅读对大家来说不会产生障碍,接下来就是找到报错的地方
(initramfs 中存在许多天然断点,这些断点可以通过在 GRUB 参数中添加 break=[break_point]
来触发。break_point
的取值包括以下几种:
- top:initramfs 启动过程的最开始阶段。
- modules:加载必要的内核模块阶段。
- premount:挂载根文件系统之前的准备阶段。
- mount:挂载根文件系统的阶段。
- mountroot:挂载根文件系统的具体实现阶段。
- bottom:initramfs 启动过程的最后阶段。
通过在特定阶段暂停启动过程,可以方便地检查和干预启动过程)。
我一般习惯先开最后,再看开头,翻到最后面,可以明显看到是 initramfs 阶段找不到对应的init
进程(其实就是找不到系统的/
),所以就单独启动了一个sh
终端,方便使用者自行分析。
系统无法启动的原因已经找到,接下来就是查看为何找不到init
进程,这需要我们找到最初开始报错的地方,查找起来实际还是很轻松的,如下图,果然是这里异常了,知道了问题我们在下次启动时,手动调整对应的grub
参数即可解决.
分阶段调试
前面说了,initramfs 中存在很多maybe_break
的天然断点,按照先后执行顺序如下所示:
- 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
命令,此时系统会继续启动。
分析 initrd内容
当我们安装内核获驱动时,我们一般会执行 update-initramfs
命令,生成新的 initrd
,偶尔也会出现生成的initrd
内容异常的问题.我们可以对比 update-initramfs 前后的 initrd 内容.
initrd 存放于 /boot/initrd.img-*
,大小一般在200MB以内, 通过lsinitramfs
命令查看其内容,通过对比更新前后的 initrd 可以发现绝大部分问题,
如果确实是update-initramfs
命令执行后 initrd 内容出现了问题,也不要恐慌,update-initramfs 也只是一个普通的 bash 脚本而已,分析其源码实现即可,这也是我们了解系统启动过程的一个绝好机会.
卡死无输出,无日志,键鼠无反应
实话说,很少见,一般都出现在未适配的硬件上面,这种情况需要电脑有串口(电脑没有串口怎么办?凉拌),通过串口查询内核输出的日志,串口的使用网络上有很多资料,因为问题不太常见所以在碰到后您可以直接百度搜索如何配置串口参数,并联系专业的内核研发人员陪同定位.
启动阶段
在 grub 编辑阶段,添加参数 init=/bin/bash
,此时处于刚挂载根文件系统,系统中的其他进程都没还启动。
添加参数 systemd.debug-shell=1
,在启动后,systemd 会占用 tty9,可以按 Ctrl + Alt + F9 进入此终端,此时用户默认具有 root 权限。
添加参数 break=bottom
,然后在接下来的 sh 环境中,先找到根文件系统,之后设置 /etc/systemd/system/default.target
指向的不同 target,可以让后续 systemd 在启动时运行到不同的级别。
或者添加参数 systemd.unit=multi-user.target
,效果和上面的操作等同,您也可以设置为其他的 target。
写在最后
想到哪里就记到哪里了,后续再随时补充…