这个问题在centos7.6 和centos8.3都遇到过,以cenots7.6为例。卡死的过程是:centos7.6虚拟机里从红帽官网下载linux-3.10.0-957.27.2.el7内核源码后,按照命令make -j8 all;make modules_install -j8;make install -j8编译内核后重启,直接卡死,必现,什么打印都没有。莫名其妙,一脸懵逼!本文主要记录这个问题的排查和解决过程。

编译内核源码并安装后,在/boot主要生成了两个文件,/boot/vmlinuz-3.10.0 和/boot/initramfs-3.10.0.img,一个是内核镜像一个是内存型文件系统。在内核启动的第一阶段,就要加载并解压initramfs-3.10.0.img,作为根文件系统,做一些检查后才会挂载真正的磁盘根文件系统/dev/sda3(根文件系统在sda3盘)。

调试这个卡死问题就很麻烦,首先这是虚拟机,卡死时调试信息没有。如果要是在开发板调试,内核启动过程调试信息很多,我也可以在内核启动关键阶段添加调试信息,协助定位卡死位置,这些在虚拟机都实现不了!于是想到用qemu调试内核,使用如下命令:

qemu-kvm -nographic -kernel /boot/vmlinuz-3.10.0 -initrd /boot/initramfs-3.10.0.img -append "root=/dev/sda3 init=/init console=ttyS0"

最后打印如下,也卡死了

1. [    1.203026] NET: Registered protocol family 1
2. [    1.204665] pci 0000:00:00.0: Limiting direct PCI/PCI transfers
3. [    1.206685] pci 0000:00:01.0: PIIX3: Enabling Passive Release
4. [    1.209278] pci 0000:00:01.0: Activating ISA DMA hang workarounds
5. [    1.212477] Unpacking initramfs...

我不知道是方法不对,还是真的用qemu复现卡死了。不过” Unpacking initramfs...”却给了我一个灵感,我认为编译新内核后生成配套的/boot/initramfs-3.10.0.img有问题,于是跟centos7.6自带的initramfs-3.10.0-957.27.2.el7.x86_64.img解压后对比了一下,没想到还真发现了问题。

解压后,initramfs-3.10.0.img的usr/lib/moduls/3.10/kernel 目录下的ko文件大小总结竟然有100多M,而initramfs-3.10.0-957.27.2.el7.x86_64.img的usr/lib/moduls/3.10.0-957.27.2.el7.x86_64/kernel 目录下的ko文件大小总结只有几十M,小了很多。再进一步查看,initramfs-3.10.0-957.27.2.el7.x86_64.img的usr/lib/moduls/3.10.0-957.27.2.el7.x86_64/kernel目录下的ko竟然被压缩了,是xz格式,如ext4.ko.xz,那怪不得usr/lib/moduls/3.10.0-957.27.2.el7.x86_64/kernel目录下的ko文件占空间少了很多。

我感觉已经找到原因了,在内核启动过程,要解压initramfs-3.10.0.img,而initramfs-3.10.0.img的usr/lib/moduls/3.10/kernel 目录下的驱动ko比正常的大了几倍,难道内存越界了?initramfs大小是有限制的!怎么验证我的想法?

首先,在我们make -j8 all;make modules_install -j8后,在/usr/lib/modules/3.10.0/kernel/目录就会生成本次编译内核的ko文件,在这个目录运行如下脚本会得到所有的ko文件及路径

1. #bin/bash
2. function listfiles()
3. {
4.         for file in `ls $1`;do
5.             if [ -d "$1/file" ];then
6.                     listfiles $1/file
7.                 else if [ "${file##*.}" == "ko" ];then
8.                          echo "$1/$file"
9.                     fi
10.                 fi
11.         done
12. }
13. listfiles .

有了所有ko的路径,对他们依次执行xz进行压缩,压缩后ko源文件不再保留,比如fs/ext4/ext4.ko变为fs/ext4/ext4.ko.xz。然后再执行make install -j8安装内核,安装过程会把/usr/lib/modules/3.10.0/kernel/目录下已经压缩的ko打包到/boot/initramfs-3.10.0.img。等搞定后reboot重启,终于可以顺利进入控制台了!问题解决。

注意,这个问题在centos8有更好的解决方案,只用内核编译时make menuconfig配置上CONFIG_MODULE_COMPRESS和CONFIG_MODULE_COMPRESS_XZ,在编译安装内核时自动压缩ko为xz格式,然后打包到initramfs镜像,不用人为参与,效率增加了不少!