以物为师:苦寻黑屏背后的元凶

书接上回,继续探索系统卡在INITRAMFS而黑屏的原因。

分析错误消息

在坠入initramfs命令行之前,init脚本给出了三行错误:

/init: line 73: wait-for-root: not found
/init: line 932: logsave: not found
The root filesystem on /dev/nvme0n1p2 requires a manual fsck

前两行都是抱怨缺文件,一个是wait-for-root,一个是logsave

在initramfs命令行中cd /在ls -l观察目录结构。

(initramfs) ls -l
drwxr-xr-x   14      3560 dev
drwx------    2         0 root
drwxr-xr-x    5         0 usr
lrwxrwxrwx    1         7 bin -> usr/bin
drwxr-xr-x    3         0 conf
drwxr-xr-x    5         0 etc
-rwxr-xr-x    1      7686 init
lrwxrwxrwx    1         7 lib -> usr/lib
drwxr-xr-x    5       100 run
lrwxrwxrwx    1         8 sbin -> usr/sbin
drwxr-xr-x    7         0 scripts
-rwx------    1   9157022 initrd.image
dr-xr-xr-x   15         0 sys
dr-xr-xr-x  230         0 proc
drwxr-xr-x    2         0 tmp
drwxr-xr-x    3         0 var

再用find命令查找wait-for-root和logsave,确实没有。

(initramfs) find -name ls
./usr/bin/ls
(initramfs) find -name wait-for-root
(initramfs) find -name logsave

以物为师

如果不使用bootyl nvme命令,那么幽兰是可以顺利启动的。为了能和成功的情况进行对照,我在u-boot命令行里修改内核参数,增加break=mount debug, 让它停在mount动作之后,并且启用init脚本的调试支持。

ulan# env edit nvmeargs
env cmd address 00000000edd4021c
edit: setenv bootargs root=${nvmeroot} rootfstype=ext4 rw console=ttyFIQ0,1500000 psi=1 rootwait quiet splash earlyprintk earlyprintk=ttyFIQ0,1500000 verbose nokaslr break=mount debug

这样修改后,再敲boot,让系统启动,并且也停在了initramfs命令行。

[   72.116411] hid-generic 0003:1C4F:007C.0002: input,hiddev96,hidraw1: USB HID v1.10 Mouse [SIGMACHIP USB Keyboard] on usb-fc880000.usb-1.1/input1




BusyBox v1.36.1 (Ubuntu 1:1.36.1-3ubuntu1) built-in shell (ash)
Enter 'help' for a list of built-in commands.


(initramfs) [  102.214024] vcc3v3_pcie_phy1: disabling

这一次,没有了错误信息。而且确实可以找到wait-for-root命令。

cd /
(initramfs) ls -l
drwxr-xr-x   14      3560 dev
drwx------    2         0 root
drwxr-xr-x    5         0 usr
lrwxrwxrwx    1         7 bin -> usr/bin
drwxr-xr-x    3         0 conf
drwxr-xr-x    5         0 etc
-rwxr-xr-x    1      7686 init
lrwxrwxrwx    1         7 lib -> usr/lib
drwxr-xr-x    4        80 run
lrwxrwxrwx    1         8 sbin -> usr/sbin
drwxr-xr-x    7         0 scripts
drwxr-xr-x    4         0 var
dr-xr-xr-x   15         0 sys
dr-xr-xr-x  248         0 proc
drwxr-xr-x    2         0 tmp
(initramfs) find -name wait-for-root
./usr/sbin/wait-for-root
(initramfs) find -name logsave
./usr/sbin/logsave

尝试执行wait-for-root,也是可以的:

(initramfs) wait-for-root
Usage: wait-for-root DEVICE TIMEOUT

查看/run/initramfs/initramfs.debug文件可以看到脚本执行过程:

(initramfs) cd /run
(initramfs) cd initramfs
(initramfs) ls
initramfs.debug
(initramfs) cat initramfs.debug
+ unset log_output
+ maybe_break top
+ run_scripts /scripts/init-top
+ initdir=/scripts/init-top
+ '[' '!' -d /scripts/init-top ]
+ shift
+ . /scripts/init-top/ORDER
+ /scripts/init-top/all_generic_ide
+ '[' -e /conf/param.conf ]
+ /scripts/init-top/blacklist
+ '[' -e /conf/param.conf ]
+ /scripts/init-top/udev
Starting systemd-udevd version 253.5-1ubuntu6.1
+ '[' -e /conf/param.conf ]
+ maybe_break modules

而且,我注意到,此时的sbin目录里明显文件更多。

(initramfs) ls -l
-rwxr-xr-x    1     67784 blkid
-rwxr-xr-x    1    404568 dhcpcd
-rwxr-xr-x    1    203448 dmsetup
-rwxr-xr-x    1     67680 dumpe2fs
-rwxr-xr-x    1    413720 e2fsck
-rwxr-xr-x    1     67792 fsck
lrwxrwxrwx    1         6 fsck.ext4 -> e2fsck
-rwxr-xr-x    1     67520 logsave
lrwxrwxrwx    1         9 modprobe -> /bin/kmod
-rwxr-xr-x    1     67736 mount.fuse3
lrwxrwxrwx    1        12 mount.ntfs -> /bin/ntfs-3g
lrwxrwxrwx    1        12 mount.ntfs-3g -> /bin/ntfs-3g
lrwxrwxrwx    1         9 rmmod -> /bin/kmod
-rwxr-xr-x    1     67512 wait-for-root

错误时的sbin目录只有下面几个文件:

(initramfs) ls -l
-rwxr-xr-x    1     67784 blkid
-rwxr-xr-x    1    404568 dhcpcd
-rwxr-xr-x    1    203448 dmsetup
-rwxr-xr-x    1     67680 dumpe2fs
-rwxr-xr-x    1    413720 e2fsck
-rwxr-xr-x    1     67792 fsck

而且fsck命令无法执行

(initramfs) fsck
sh: fsck: Text file busy

INITRAMFS命令行

在initramfs的命令行,可以执行常用的外壳命令(shell),以及initramfs镜像里自带的程序,比如fsck等。

准确说,外壳命令是busybox版本,具体命令多少,可能略有差异。下面是幽兰上的情况。

(initramfs) help
Built-in commands:
------------------
        . : [ alias break cd chdir command continue echo eval exec exit
        export false getopts hash help history let local printf pwd read
        readonly return set shift sleep test times trap true type ulimit
        umask unalias unset wait [ [[ acpid arch ascii ash awk base32
        basename blockdev busybox cat chmod chroot chvt clear cmp cp
        crc32 cut date deallocvt deluser devmem df dirname du dumpkmap
        echo egrep env expr false fbset fgrep find fold fstrim grep gunzip
        gzip hostname hwclock i2ctransfer ifconfig ip kill ln loadfont
        loadkmap ls lzop mkdir mkfifo mknod mkswap mktemp modinfo more
        mount mv nuke openvt pidof printf ps pwd readlink reboot reset
        rm rmdir run-init sed seq setkeycodes sh sleep sort stat static-sh
        stty switch_root sync tail tee test touch tr true ts tty umount
        uname uniq wc wget which yes

重做INITRAMFS

启动时卡在initramfs的情况不少人都遇到过。搜一下互联网,可以找到很多讨论。特别是搜索第三行报错信息,搜索结果超过4万。

The root filesystem on /dev/nvme0n1p2 requires a manual fsck

其中askubuntu网站的一个帖子,有问题,有方案,而且有人使用这个方子成功过。

Thanks! Indeed, that fixed it. Learned something new today, too. – 
Nate
 CommentedApr 1, 2023 at 21:21

https://askubuntu.com/questions/1461061/zstd-compressed-data-is-corrupt-root-filesystem-requires-a-manual-fsck

简单来说,这个方案是使用update-initramfs命令重做initramfs镜像。

我按方案步骤做了一遍,但是没有效果。

骗人的根因

init脚本使用很多环境变量,其中一个名为REASON的环境变量是专门用来提示失败原因的,以便帮助调试。在initramfs命令行,可以通过echo命令观察这个变量:

(initramfs) echo $REASON
The root filesystem on /dev/nvme0n1p2 requires a manual fsck

其实它也就是第三条报错信息。

当我后来找到正确答案后再回过头来看,这个REASON信息纯粹就是误导。这条信息说/dev/nvme0n1p2需要检查,其实根本不需要。之所以init脚本给出这么个原因,是因为缺少logsave命令,没法执行fsck命令了。而不是fsck检查到磁盘错误。

且看只要checkfs_once的返回值为0,那么就是调用panic函数。

checkfs()
{
        while ! _checkfs_once "$@"; do
                panic "The $2 filesystem on $1 requires a manual fsck"
        done
}

下面是开启debug后的脚本执行过程(来自/run/initramfs/initramfs.debug)。

Begin: Will now check root file system ... + return 0
+ logsave -a -s /run/initramfs/fsck.log fsck -a -V -t ext4 /dev/nvme0n1p2
/init: line 926: logsave: not found
+ FSCKCODE=127
+ log_end_msg
+ _log_msg 'done.\n'
+ '[' n '=' y ]
+ printf 'done.\n'
done.
+ return 0
+ '[' 127 -eq 32 ]
+ '[' 4 -eq 4 ]
+ log_failure_msg 'File system check of the root filesystem failed'
+ _log_msg 'Failure: %s\n' 'File system check of the root filesystem failed'
+ '[' n '=' y ]
+ printf 'Failure: %s\n' 'File system check of the root filesystem failed'
Failure: File system check of the root filesystem failed
+ return 0
+ return 1
+ panic 'The root filesystem on /dev/nvme0n1p2 requires a manual fsck'
+ local console rest IFS
+ command -v chvt
+ chvt 1
+ echo 'The root filesystem on /dev/nvme0n1p2 requires a manual fsck'
The root filesystem on /dev/nvme0n1p2 requires a manual fsck

综上分析,我们用以物为师的方法,验证了三条“错误信息”中,前两条陈述是真实的,也是有价值的。而第三条是错误的,是骗人的。这再次说明,调试问题时,我们应该更关注那些来自故障第一现场,带有特殊细节的“实物”和朴实报告,越鲜活越好,越特别越好,不要轻信那些归纳过的,二手的,笼统的,花哨的概括性信息。这刚好与调查事故现场的原则是相同的。

34fe2babca02ae749ad3c292dea8d2cb.png

挑战者号残骸 (来自NASA官网)

(未完待续)

(写文章很辛苦,恳请各位读者点击“在看”,欢迎转发)

*************************************************

正心诚意,格物致知,以人文情怀审视软件,以软件技术改变人生

扫描下方二维码或者在微信中搜索“盛格塾”小程序,可以阅读更多文章和有声读物

31c3015a194c82d9663df25a01aaa204.png

也欢迎关注格友公众号

3ab884a83251aca0dc4a418a06d68f7d.jpeg

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值