0x01 序※
晚上一次正常的更新系统途中kernel panic导致系统爆炸的分析与修复。
0x02 背景&症状※
-` sa@archlinux
.o+` ------------
`ooo/ OS: Arch Linux x86_64
`+oooo: Host: ROG Strix G531GU 1.0
`+oooooo: Kernel: 6.8.1-arch1-1-lily
-+oooooo+: Uptime: 14 hours
`/:-:++oooo+: Packages: 1990 (pacman)
`/++++/+++++++: Shell: zsh 5.9
`/++++++++++++++: Resolution: 1920x1080
`/+++ooooooooooooo/` DE: Plasma 6.0.4
./ooosssso++osssssso+` WM: KWin
.oossssso-````/ossssss+` Theme: Breeze Light [Plasma], Breeze [GTK2/3]
-osssssso. :ssssssso. Icons: [Plasma], breeze [GTK2/3]
:osssssss/ osssso+++. Terminal: konsole
/ossssssss/ +ssssooo/- CPU: Intel i7-9750H (12) @ 4.500GHz
`/ossssso+/:- -:/+osssso+- GPU: NVIDIA GeForce GTX 1660 Ti Mobile
`+sso+:-` `.-/+oso: GPU: Intel CoffeeLake-H GT2 [UHD Graphics 630]
`++:. `-/+/ Memory: 3686MiB / 15834MiB
.` `/
滚系统时候图形卡死,CapsLock
键闪烁,切不了TTY,典型的Kernel Panic,尝试SysRQ恢复没反应(好像开了?)。只能长按电源键重启,此时还没有意识到问题的严重性。
重启后黑屏,依旧无法进入TTY,猜测是更新了驱动有问题或者更新时候更到Xorg或者KDE桌面挂掉了,无法操作只能强制重启。
0x03 恢复控制权※
尝试进入恢复模式,重新掌控电脑,我使用的System-Boot引导,启动按e编辑启动项。更改启动目标,行末加上内核参数systemd.unit=multi-user.target
(相当于sysV的level 3)进入多用户模式。这时候多用户模式目标(Multi-user.target)会连接到Systemd的默认Target,在这个状态下系统不会启动图形系统(也就是Xorg SDDM KDE plasma都不会启动)最容易排查是那个部分炸掉了。
如果multi-user.target依旧进不去那就比较危险,可能是驱动不兼容炸的,只能进到更低的运行级别rescue.target(单用户模式相当于SysV的level 1)或者emergency.target 急救模式;如果能进去,就能得到足够的错误信息判断是图形系统炸了还是依赖的什么东西炸了。
很庆幸我进去了。
0x04 诊断与修复※
还没登陆,启动过程就给我了几个红色信息
5月 14 21:25:08 archlinux systemd[1]: Listening on D-Bus System Message Bus Socket.
5月 14 21:25:08 archlinux systemd[1]: Listening on GnuPG network certificate management daemon for /etc/pacman.d/gnupg.
5月 14 21:25:08 archlinux systemd[1]: Starting Docker Socket for the API...
5月 14 21:25:08 archlinux systemd[1]: Listening on GnuPG cryptographic agent and passphrase cache (access for web browsers) for /etc/pacman.d/gnupg.
5月 14 21:25:08 archlinux systemd[1]: Listening on GnuPG cryptographic agent and passphrase cache (restricted) for /etc/pacman.d/gnupg.
5月 14 21:25:08 archlinux systemd[1]: Listening on GnuPG cryptographic agent (ssh-agent emulation) for /etc/pacman.d/gnupg.
5月 14 21:25:08 archlinux systemd[1]: Listening on GnuPG cryptographic agent and passphrase cache for /etc/pacman.d/gnupg.
5月 14 21:25:08 archlinux systemd[1]: Listening on GnuPG public key management service for /etc/pacman.d/gnupg.
5月 14 21:25:08 archlinux systemd[1]: Listening on libvirt legacy monolithic daemon socket.
5月 14 21:25:08 archlinux systemd[1]: Listening on libvirt legacy monolithic daemon admin socket.
5月 14 21:25:08 archlinux systemd[1]: Listening on libvirt legacy monolithic daemon read-only socket.
5月 14 21:25:08 archlinux systemd[1]: Listening on libvirt locking daemon socket.
5月 14 21:25:08 archlinux systemd[1]: Listening on libvirt logging daemon socket.
5月 14 21:25:08 archlinux systemd[1]: Starting D-Bus System Message Bus...
5月 14 21:25:08 archlinux systemd[1]: TPM2 PCR Barrier (Initialization) was skipped because of an unmet condition check (ConditionSecurity=measured-uki).
5月 14 21:25:08 archlinux systemd[1]: Listening on Docker Socket for the API.
5月 14 21:25:08 archlinux systemd[1]: Reached target Socket Units.
5月 14 21:25:08 archlinux dbus-broker-launch[654]: Invalid XML in /usr/share/dbus-1/system.d/org.kde.kf6auth.conf +1: no element found
5月 14 21:25:08 archlinux dbus-broker-launch[654]: ERROR run @ ../dbus-broker-36/src/launch/main.c +152: Return code 1
5月 14 21:25:08 archlinux dbus-broker-launch[654]: main @ ../dbus-broker-36/src/launch/main.c +178
5月 14 21:25:08 archlinux dbus-broker-launch[654]: Exiting due to fatal error: -131
5月 14 21:25:08 archlinux systemd[1]: dbus-broker.service: Main process exited, code=exited, status=1/FAILURE
5月 14 21:25:08 archlinux systemd[1]: dbus-broker.service: Failed with result 'exit-code'.
5月 14 21:25:08 archlinux systemd[1]: Failed to start D-Bus System Message Bus.
看上去好像DBus炸了,可能因为更新了一半导致的。我尝试重新更新系统,但是NetworkManager没有启动,没有网络。
我尝试启动nm得到
archlinux systemd[1]:
Dependency failed for Network Manager.
archlinux systemd[1]:
NetworkManager
.service: Job NetworkManager.service/start failed with result 'dependency'.
因为依赖不满足,启动失败。好像启动nm依赖DBus,但DBus已经炸了,回去查看DBus的错误
archlinux dbus-broker-launch[654]: Invalid XML in /usr/share/dbus-1/system.d/org.kde.kf6auth.conf +1: no element found
archlinux dbus-broker-launch[654]: ERROR run @ ../dbus-broker-36/src/launch/main.c +152: Return code 1
archlinux dbus-broker-launch[654]: main @ ../dbus-broker-36/src/launch/main.c +178
archlinux dbus-broker-launch[654]: Exiting due to fatal error: -131
archlinux systemd[1]:
dbus-broker.service: Main process exited, code=exited, status=1/FAILURE
archlinux systemd[1]:
dbus-broker.service: Failed with result 'exit-code'.
archlinux systemd[1]:
Failed to start D-Bus System Message Bus.
由于dbus-broker
主进程退出导致的DBus启动失败。
再往上看由于无效的XML语法在/usr/share/dbus-1/system.d/org.kde.kf6auth.conf
文件,没找到该有的元素
我cd进去/usr/share/dbus-1/system.d/
查看只有org.kde.kf5auth.conf
,没有6。
打开org.kde.kf5auth.conf
看看:
<!DOCTYPE busconfig PUBLIC
"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<!-- Allow anyone to call into the service - we'll reject callers using Polkit -->
<policy context="default">
<allow send_interface="org.kde.kf5auth"/>
<allow receive_sender="org.kde.kf5auth"/>
<allow receive_interface="org.kde.kf5auth"/>
</policy>
</busconfig>
看上去不是复杂的文件,像是dbus和Plasma接口的配置文件,那么既然更新了Plasma 6,配置也应该是6,接口名一般也不会有太大的变化。
于是我cp org.kde.kf5auth.conf org.kde.kf6auth.conf
,把里面的5都改成6。重启DBus!
得到:
archlinux systemd[1]: Starting D-Bus System Message Bus...
archlinux dbus-broker-launch[1224]: Invalid XML in /usr/share/dbus-1/system.d/org.kde.
ktexteditor6
.katetextbuffer.conf +1: no element found
archlinux dbus-broker-launch[1224]: ERROR run @ ../dbus-broker-36/src/launch/main.c +152: Return code 1
archlinux dbus-broker-launch[1224]: main @ ../dbus-broker-36/src/launch/main.c +178
archlinux dbus-broker-launch[1224]: Exiting due to fatal error: -131
archlinux systemd[1]:
dbus-broker.service: Main process exited, code=exited, status=1/FAILURE
archlinux systemd[1]:
dbus-broker.service: Failed with result 'exit-code'.
archlinux systemd[1]:
Failed to start D-Bus System Message Bus.
又变成org.kde.ktexteditor6.katetextbuffer.conf
了,如法炮制,重启!
这次进到TTY没有红色了,看一眼D-Bus,启动了,没有什么大问题。
尝试启动SDDM进入桌面
得到:
archlinux systemd[1]: Started Simple Desktop Display Manager.
archlinux sddm[867]:
Initializing...
archlinux sddm[867]:
Starting...
archlinux sddm[867]:
Logind interface found
archlinux sddm[867]:
Adding new display...
archlinux sddm[867]:
Loaded empty theme configuration
archlinux sddm[867]:
Xauthority path: "/run/sddm/xauth_hvJEhW"
archlinux sddm[867]:
Using VT 2
archlinux sddm[867]:
Display server starting...
archlinux sddm[867]:
Writing cookie to "/run/sddm/xauth_hvJEhW"
archlinux sddm[867]:
Running: /usr/bin/X -nolisten tcp -background none -seat seat0 vt2 -auth /run/sddm/xauth_hvJEhW -noreset -displayfd 16
archlinux sddm[867]:
Failed to read display number from pipe
archlinux sddm[867]:
Display server stopping...
archlinux sddm[867]:
Attempt 1 starting the Display server on vt 2 failed
archlinux sddm[867]:
Display server starting...
archlinux sddm[867]:
Writing cookie to "/run/sddm/xauth_hvJEhW"
archlinux sddm[867]:
Running: /usr/bin/X -nolisten tcp -background none -seat seat0 vt2 -auth /run/sddm/xauth_hvJEhW -noreset -displayfd 16
archlinux sddm[867]:
Failed to read display number from pipe
archlinux sddm[867]:
Display server stopping...
archlinux sddm[867]:
Attempt 2 starting the Display server on vt 2 failed
archlinux sddm[867]:
Display server starting...
archlinux sddm[867]:
Writing cookie to "/run/sddm/xauth_hvJEhW"
archlinux sddm[867]:
Running: /usr/bin/X -nolisten tcp -background none -seat seat0 vt2 -auth /run/sddm/xauth_hvJEhW -noreset -displayfd 16
archlinux sddm[867]:
Failed to read display number from pipe
archlinux sddm[867]:
Display server stopping...
archlinux sddm[867]:
Attempt 3 starting the Display server on vt 2 failed
archlinux sddm[867]:
Could not start Display server on vt 2
archlinux sddm[867]:
Signal received: SIGTERM
X11还是炸的sddm拉不起来,错误的意思大概是X11因为不能从管道中读取显示器编号而启动失败。
我删除了/etc/X11/*.conf
和/etc/X11/conf.d/*.conf
但还是无效(这在遇到x11不能启动的情况经常有效,删除conf文件记得备份)。
查看日志发现Optimus-Manager也炸了,这块和硬件相关、电源策略相关我就不了解了,先禁用optimus-manager
我这里有个旧病就是独显(Nvida)会被当作i2c设备,一启动图形就死机,解决方法是禁用
i2c_nvidia_gpu
模块而且不能用BBswitch,通过BBswitch关闭显卡电源会因为电源状态错误死机,解决方法就是不用BBswitch
有时间单开一篇写写
刚才更新的时候有很多kde的包在更新,我怀疑是不完全更新导致的,于是我使用pacman -Qkk
检查软件包状态
结果查到大量如下内容
...
ldconfig: File /usr/lib/libKF6UnitConversion.so is empty, not checked.
ldconfig: File /usr/lib/libKF6PeopleWidgets.so is empty, not checked.
ldconfig: File /usr/lib/libQuickChartsControls.so is empty, not checked.
error: error while reading file /var/lib/pacman/local/gupnp-dlna-0.10.5+4+gc947eed-2/mtree: Unrecognized archive format
error: error while reading file /var/lib/pacman/local/gvfs-1.46.1-1/mtree: Unrecognized archive format
...
ldconfig: File /path/to/file is empty, not checked
意思是pacman的文件追踪器与文件系统中实际的文件不对应,当追踪器与实际不符时pacman不会覆盖与数据库不一致的文件,如果是空就会跳过检查。所以强制更新这些软件包不奏效因为不一致的文件已经跳过检查了。对此的解决方法是pacman -Qkk
检查有问题的包使用pacman --overwrite
参数强制覆盖不匹配的文件
error: error while reading file /path/to/mtree: Unrecognized archive format
意味着pacman的数据库已经损坏,不能反应这些包的文件结构,这时pacman -Qnq
也不会显示损坏的软件包。这通常是文件系统出现大规模的严重错误、磁盘损坏等导致的。
先修复数据库使用pacman --dbonly
参数
pacman --dbonly -S <all packages with broken mtrees>
可以使用
LC_ALL=C pacman -Qkk 2>/dev/null | sed '/no mtree/!d; s/:.*//g' |pacman --dbonly -S -
来检查所有软件包然后修复数据库。
由于错误的软件包太多了(Qkk检查出900多个有问题的包)我决定重新安装所有软件包
sudo pacman -Syu --overwrite '\*' $(pacman -Qnq)
重新安装1989个包(我都不知道居然有这么多,一直以为就900多个)
经过漫长的等待之后终于滚完了。然后重新启动!
插电!开机!直接进入sddm。成功啦!
0x05 跋※
Panic时候用不了SysRq,检查一下是不是开着
archlinux# cat /proc/sys/kernel/sysrq
16
16是只允许sync同步磁盘(这是arch 的默认值吗?我记得我开的是1)
开起来!
小知识:
什么是SysRq?
神奇的 SysRq 键是 Linux 内核可以理解的组合键,它允许用户执行各种低级命令,无论系统状态如何。它通常用于从冻结中恢复,或在不损坏文件系统的情况下重新启动计算机。
怎么开启SysRq?
如果只是临时更改
SysRq
的值,只需要将新的值写入到/proc/sys/kernel/sysrq
中去
echo "1" |sudo tee /proc/sys/kernel/sysrq
或者通过
sysctl
来进行设置sysctl -w kernel.sysrq=1
如果需要每次启动时都自动修改
SysRq
的值,则需要修改配置文件
echo "kernel.sysrq = 1"|sudo tee -a /etc/sysctl.conf
怎么用SysRq?
SysRq 键在 QWERT 键盘上与
PrtSc
同键,通过按下ALT+SysRq+<command key>
可以直接向linux kernel发送预设的系统操作指令。R-E-I-S-U-B:安全重启系统
这套组合键大致相当于
reboot
命令:
- ALT+SysRq+R:
unRaw
– 把键盘设置为XLATE
模式,使按键可以穿透 x server 捕捉传递给内核- ALT+SysRq+E:
tErminate
– 向除 init 外进程发送SIGTERM
信号,让其自行结束. 这一步推荐等待30秒让进程有足够的时间进行收尾的工作。- ALT+SysRq+I:
kIll
- 向除init
以外所有进程发送SIGKILL
信号,强制结束进程. 这一步推荐等待10秒,保证所有进程都退出了- ALT+SysRq+S:
Sync
– 同步缓冲区数据到硬盘,避免数据丢失. 这一步在能看到输出的情况下等到"Emergency Sync complete" 后再做后续动作,否则推荐等待10秒- ALT+SysRq+U:
Unmount
– 将所有已经挂载的文件系统 重新挂载为只读. 该操作通常也有一定延时,请等到"Emergency Remount complete" 出现过后再进行后续操作,否则推荐等待10秒- ALT+SysRq+B:
reBoot
- 立即重启计算机恢复系统挂起
若仅仅是因为资源消耗过量引起系统挂起就重启系统显然是不好的,我们可以尝试通过回收一些资源的方式来回复系统挂起。
SysRq
中用来结束进程的command-key
包括 E-I-K-F,其中:
- ALT+SysRq+E 和 I 太凶残,它会杀掉除了 init 外的所有进程,属于杀敌一千自损八百的操作。因此在一般情况下不会轻易使用
- ALT+SysRq+F 则是利用 OOM-Kiler选择一个进程来结束,对于由于内存不足引起的挂起比较有效,但有时候OOMKiller也可能会误判杀掉一些长期运行的后台程序。
- ALT+SysRq+K 杀掉与当前控制台有关的进程组,比较推荐用这种方法回复系统
此外,若系统挂起是由于实时任务消耗太多CPU引起的,则可以通过
N
来降低实时任务运行的优先级来缓解挂起症状。
SysRq数字与功能对应关系:
0 | 完全禁用sysrq | |
1 | 允许所有的sysrq功能 | |
2 | 允许控制终端日志级别 | |
4 | 允许控制键盘输入类型(SAK,unraw) | |
8 | 允许调试进程dump | |
16 | 允许执行sync命令 | |
32 | 允许重新挂载文件系统为只读 | |
64 | 允许发送信号给进程(term,kill,oom-kill) | |
128 | 允许重启/关机 | |
256 | 允许调整实时任务的优先级 |