linux 发行版内核裁剪之内核模块裁剪

内核裁剪的目标

以前我一直想搞搞内核裁剪相关的东西,今天就抓住机会来搞一下。目标是在两个小时之内完成,只保留我的 linux 中需要使用的内核模块配置,其它的都删除。

为什么我要进行内核裁剪

玩内核裁剪的初衷在于我想编译主机上的 linux 内核,但因为它需要编译非常多的内核模块,编译时间太长只能放弃。

我现在正在使用的 relase 内核中模块数目统计如下:

[longyu@debian-10:08:24:33]  $ find  /lib/modules/4.19.0-10-amd64/ -name '*.ko' | wc
   3559    3559  243267

可以看到总共有 3559 个内核模块,而我真正需要用的其实也就几百个左右。

lsmod 统计信息如下:

[longyu@debian-10:08:25:01] $ lsmod | wc
    189     660    7958

可以看到我在用的只有 189 个内核模块,这就是我裁剪内核的动机所在。

make localmodconfig 大法

目标与要求都描述清楚后,我还必须说明时间上的要求。2 个小时的时间其实非常短,这只是我在睡觉前能够抽出来的最多的时间。

如果手动去做,两个小时肯定不够,但是我之前阅读一些 linux 书籍的时候发现可以生成一个系统正在使用的内核模块的配置文件。

执行如下命令就可以生成:

make localmodconfig

我相信有了这个知识,两个小时应该能够达成我的目标。

make localmodconfig 使用的注意事项

在 make localmodconfig 执行前,需要拷贝本机正在使用的内核的 config 文件到内核源码树中的 .confg 中。

示例过程如下:

[longyu@debian-10:08:37:57] linux-source-4.19 $ cp /boot/config-4.19.0-10-amd64 ./.config
[longyu@debian-10:08:38:03] linux-source-4.19 $ make localmodconfig 
using config: '.config'

注意不能使用 make defconfig 生成的 .config 文件!!

使用新的 .config 文件编译内核并安装内核模块

编译完成后,执行 make modules_install 安装编译出的内核模块。在我的系统中,新的内核模块安装完成后,统计信息如下:

[longyu@debian-10:08:41:33] linux-source-4.19 $ find /lib/modules/4.19.98/ -name '*.ko' | wc
    240     240   13653

可以看到现在只有 240 个内核模块,相比三千多个,这个数目已经相当可观了。

重新制作新的 initrd

手动制作 initrd 文件

我最开始手动制作了 initrd 文件,制作方法如下:

  1. 拷贝系统正在使用的 initrd 文件
  2. 解压 initrd 文件,删除 usr/lib/modules 中的模块子目录,复制新安装的模块子目录到 usr/lib/modules 中
  3. 制作新的文件列表并使用 cpio 生成新的 initrd 文件
  4. 重新压缩 initrd 文件并拷贝到 /boot 目录中

手动制作的 initrd 文件测试结果

使用新的内核 bzImage 文件与手动制作的 initrd 文件测试,在 grub 中编辑相应的项目指定使用新的内核与 initrd 文件。

进入系统后图形界面正常工作,但是发现无线网卡不能使用。查看 /var/log/kern.log 发现了如下报错:

Aug 19 23:27:19 debian-10 kernel: [    6.926482] iwlwifi 0000:00:14.3: firmware: failed to load iwlwifi-9000-pu-b0-jf-b0-38.ucode (-2)
Aug 19 23:27:19 debian-10 kernel: [    6.926677] iwlwifi 0000:00:14.3: Direct firmware load for iwlwifi-9000-pu-b0-jf-b0-38.ucode failed with error -2
Aug 19 23:27:19 debian-10 kernel: [    6.926696] iwlwifi 0000:00:14.3: firmware: failed to load iwlwifi-9000-pu-b0-jf-b0-37.ucode (-2)
Aug 19 23:27:19 debian-10 kernel: [    6.926729] iwlwifi 0000:00:14.3: Direct firmware load for iwlwifi-9000-pu-b0-jf-b0-37.ucode failed with error -2
Aug 19 23:27:19 debian-10 kernel: [    6.926741] iwlwifi 0000:00:14.3: firmware: failed to load iwlwifi-9000-pu-b0-jf-b0-36.ucode (-2)
Aug 19 23:27:19 debian-10 kernel: [    6.926773] iwlwifi 0000:00:14.3: Direct firmware load for iwlwifi-9000-pu-b0-jf-b0-36.ucode failed with error -2
Aug 19 23:27:19 debian-10 kernel: [    6.926783] iwlwifi 0000:00:14.3: firmware: failed to load iwlwifi-9000-pu-b0-jf-b0-35.ucode (-2)
Aug 19 23:27:19 debian-10 kernel: [    6.926814] iwlwifi 0000:00:14.3: Direct firmware load for iwlwifi-9000-pu-b0-jf-b0-35.ucode failed with error -2
Aug 19 23:27:19 debian-10 kernel: [    6.926824] iwlwifi 0000:00:14.3: firmware: failed to load iwlwifi-9000-pu-b0-jf-b0-34.ucode (-2)
Aug 19 23:27:19 debian-10 kernel: [    6.926858] iwlwifi 0000:00:14.3: Direct firmware load for iwlwifi-9000-pu-b0-jf-b0-34.ucode failed with error -2
Aug 19 23:27:19 debian-10 kernel: [    6.926879] iwlwifi 0000:00:14.3: firmware: failed to load iwlwifi-9000-pu-b0-jf-b0-33.ucode (-2)
Aug 19 23:27:19 debian-10 kernel: [    6.926920] iwlwifi 0000:00:14.3: Direct firmware load for iwlwifi-9000-pu-b0-jf-b0-33.ucode failed with error -2
Aug 19 23:27:19 debian-10 kernel: [    6.926929] iwlwifi 0000:00:14.3: firmware: failed to load iwlwifi-9000-pu-b0-jf-b0-32.ucode (-2)
Aug 19 23:27:19 debian-10 kernel: [    6.926965] iwlwifi 0000:00:14.3: Direct firmware load for iwlwifi-9000-pu-b0-jf-b0-32.ucode failed with error -2
Aug 19 23:27:19 debian-10 kernel: [    6.926974] iwlwifi 0000:00:14.3: firmware: failed to load iwlwifi-9000-pu-b0-jf-b0-31.ucode (-2)
Aug 19 23:27:19 debian-10 kernel: [    6.927098] iwlwifi 0000:00:14.3: Direct firmware load for iwlwifi-9000-pu-b0-jf-b0-31.ucode failed with error -2
Aug 19 23:27:19 debian-10 kernel: [    6.927111] iwlwifi 0000:00:14.3: firmware: failed to load iwlwifi-9000-pu-b0-jf-b0-30.ucode (-2)
Aug 19 23:27:19 debian-10 kernel: [    6.927145] iwlwifi 0000:00:14.3: Direct firmware load for iwlwifi-9000-pu-b0-jf-b0-30.ucode failed with error -2
Aug 19 23:27:19 debian-10 kernel: [    6.927147] iwlwifi 0000:00:14.3: minimum version required: iwlwifi-9000-pu-b0-jf-b0-30
Aug 19 23:27:19 debian-10 kernel: [    6.927257] iwlwifi 0000:00:14.3: maximum version supported: iwlwifi-9000-pu-b0-jf-b0-38
Aug 19 23:27:19 debian-10 kernel: [    6.927286] iwlwifi 0000:00:14.3: check git://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git
A

上述报错表明 iwlwifi 加载固件失败,-2 表示 no such file,这应该是没有找到固件文件。我简单分析了下,发现我手动制作的 initrd 大小要比系统中正在使用的大很多,我怀疑可能是大小有影响。

临时的规避方法

在上面的问题出现后,我使用 dpdk 中的网卡绑定工具查看信息,发现无线网卡接口没有绑定驱动,这可能是固件加载失败的一个副作用。

我执行如下命令进行绑定,绑定后无线网卡可以正常使用。

[longyu@debian-10:22:23:27] ~ $ su -c  'echo "0000:00:14.3" > /sys/module/iwlwifi/drivers/pci\:iwlwifi/bind'

iwlwifi 驱动加载异常的问题

经过实验排查,排除了 initrd 大小的影响。这之后,我重新使用官方的内核与 initrd 进行引导,引导后继续查看 kern.log 文件的记录,发现有如下信息:

Aug 19 22:55:09 debian-10 kernel: [    6.409271] raid6: sse2x1   gen() 12417 MB/s
Aug 19 22:55:09 debian-10 kernel: [    6.477273] raid6: sse2x1   xor()  9925 MB/s
Aug 19 22:55:09 debian-10 kernel: [    6.545269] raid6: sse2x2   gen() 16575 MB/s
Aug 19 22:55:09 debian-10 kernel: [    6.613248] raid6: sse2x2   xor() 11259 MB/s
Aug 19 22:55:09 debian-10 kernel: [    6.681247] raid6: sse2x4   gen() 18603 MB/s
Aug 19 22:55:09 debian-10 kernel: [    6.749242] raid6: sse2x4   xor() 11906 MB/s
Aug 19 22:55:09 debian-10 kernel: [    6.817269] raid6: avx2x1   gen() 27260 MB/s
Aug 19 22:55:09 debian-10 kernel: [    6.885267] raid6: avx2x1   xor() 18969 MB/s
Aug 19 22:55:09 debian-10 kernel: [    6.953248] raid6: avx2x2   gen() 32701 MB/s
Aug 19 22:55:09 debian-10 kernel: [    7.021271] raid6: avx2x2   xor() 20823 MB/s
Aug 19 22:55:09 debian-10 kernel: [    7.089270] raid6: avx2x4   gen() 34730 MB/s
Aug 19 22:55:09 debian-10 kernel: [    7.157240] raid6: avx2x4   xor() 22804 MB/s
Aug 19 22:55:09 debian-10 kernel: [    7.157241] raid6: using algorithm avx2x4 gen() 34730 MB/s
Aug 19 22:55:09 debian-10 kernel: [    7.157241] raid6: .... xor() 22804 MB/s, rmw enabled
Aug 19 22:55:09 debian-10 kernel: [    7.157242] raid6: using avx2x2 recovery algorithm
Aug 19 22:55:09 debian-10 kernel: [    7.163216] xor: automatically using best checksumming function   avx       
Aug 19 22:55:09 debian-10 kernel: [    7.202667] Btrfs loaded, crc32c=crc32c-intel
Aug 19 22:55:09 debian-10 kernel: [    7.285982] PM: Image not found (code -22)
Aug 19 22:55:09 debian-10 kernel: [    7.411200] EXT4-fs (nvme0n1p5): mounted filesystem with ordered data mode. Opts: (null)
Aug 19 22:55:09 debian-10 kernel: [    7.447680] EXT4-fs (nvme0n1p6): mounted filesystem with ordered data mode. Opts: (null)
Aug 19 22:55:09 debian-10 kernel: [    8.264892] EXT4-fs (nvme0n1p6): re-mounted. Opts: (null)
Aug 19 22:55:09 debian-10 kernel: [    8.280733] EXT4-fs (nvme0n1p5): re-mounted. Opts: (null)

正常的 log 信息与异常的 log 信息的主要区别内容如下:

  1. 正常的 log 信息中先挂载了 nvme0n1p5 与 nvme0n1p6 分区,然后才加载了 iwlwifi 驱动。

  2. 异常的 log 信息中先加载了 iwlwifi 驱动,然后再挂载 nvme0n1p5 与 nvme0n1p6 分区。

在我的系统中 nvme0n1p6 分区与 nvme0n1p5 分区挂载点如下所示:

/dev/nvme0n1p5   30G  480M   28G    2% /
/dev/nvme0n1p6   20G   12G  7.3G   62% /usr

在我的系统中,iwlwifi 需要加载的固件信息放在如下目录中:

[longyu@debian-10:08:59:47] tmp $ locate 'iwlwifi-9000-pu-b0-jf-b0-38.ucode'
/usr/lib/firmware/iwlwifi-9000-pu-b0-jf-b0-38.ucode

有了这两个信息,问题就清楚了。iwlwifi 需要加载的固件存储在 /usr 分区中,需要挂载 nvme0n1p6 后才能访问到。

异常的执行流程中,在 /usr 分区挂载前就加载了 iwlwifi 命令并加载固件,这时候肯定要报 no such file 的错误。

什么因素导致 iwlwifi 驱动在 /usr 分区挂载前就加载了呢?

有了上面的基础,我怀疑这个问题可能是 initrd 的内容过大导致 systemd-udevd 在分区挂载前就加载了 iwlwifi 驱动

关于 systmed-udevd 加载驱动的原理,可以查看 debian 启动流程分析 这篇博文。

我重新阅读 initrd 中的初始化脚本,没有找到怀疑点。

自动生成 initrd 文件

有了上面的怀疑后,我尝试自动生成 initrd 文件,使用 update-initramfs 重新制作 initrd 后,大小为 82M,手动制作时大小为 172M。

使用自动生成的 initrd 文件重新启动系统,这次无线网卡正常工作

自动生成的 initrd 文件与手动生成的 initrd 文件内容对比

既然使用自动生成的 initrd 文件 iwlwifi 能够正常工作,那么问题可能是我手动制作的 initrd 文件与自动生成的 initrd 文件存在某些差异点,大小是表面差异,更重要的应该是内容的差异。

最后对比发现,update-initramfs 制作的 initrd 中没有放 iwlwifi.ko 驱动,并且 modules.alias 中删除了与 iwlwifi 相关的设备信息。这样在挂载 /usr 与 / 目录前都不会去加载 iwlwifi 驱动,当挂载了 /usr 与 / 后 modules.alias 更新,iwlwifi 模块存在了,这时再加载这个模块就没有问题了。

我删除手动制作的 initrd 中的 iwlwifi 驱动与 modules.alias 表格中与 iwlwifi 设备相关的描述,重新制作 initrd 后重新引导,这次正常了。

为什么 update-initramfs 生成的 initrd 更小

解决了 iwlwifi 驱动加载异常的问题后,我想到了一个新的问题:为什么 update-initramfs 生成的 initrd 更小?

对比发现,update-initramfs 生成的 initrd 中只放了部分的内核模块

研究了下脚本执行的过程,发现它并不像我手动制作 initrd 那样直接拷贝整个 lib/modules/xx 到 usr/lib/modules 中,它有很多逻辑判断,对需要拷贝到 initrd 中的内核模块进行了筛选,一定程度上依赖系统 sys 目录下的文件来完成判断过程。

总结

即便你已经想到了问题可能比你预期的多,真正操作时问题可能要比你预期的还要更多一些。

裁剪内核 10 分钟,解决 initrd 的问题花了几个小时,这就是一个再具体不过的例子。真可谓是纸上得来终觉浅,绝知此事要躬行,计算机知识的掌握还是要多多实践,纯粹了解理论可能掉入纸上谈兵的旋涡中。

同时这里我也想说明问题分析的一种方法。

  1. 首先确定问题出现的层次,异常与正常的差异在哪一层
  2. 然后将正常与异常层次的输出信息进行对比,找到差异点
  3. 排查差异点来解决问题

一些联想

以前我在搞嵌入式的时候,经常遇到与别人执行相同的操作,但是不能得到正常结果的情况,一度让我感到很郁闷。

其实在这里的相同只是表面上的,最后真正解决了问题后,我才意识到其实我们的操作上还是有差异点,只不过这个差异点太隐含,不能立刻发现。

现在我再来回答之前我提过的这样一个问题:为什么我总是能够遇到更多的问题?

可能我的运气更差吧!可是如果将时间拉长,这种差的运气最终却带来了好的结果。

因为运气好而避过了某些坑,这些坑总有一天还会出现,因为运气差而遇到了各种坑,前期肯定进展缓慢,但是经过了这个过程后,自我的能力无疑变得更加强大。在别人眼里很难的问题,可能不过是之前自己曾经踩过的坑罢了。

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值