第三章:内核启动参数调优——去掉不必要的内核模块与驱动

大家好,我是老赵。做PDA启动优化这么多年,我最大的体会就是:内核启动参数的调优,是性价比最高的优化手段之一。你想想看,一个臃肿的内核,加载了成百上千个根本用不上的驱动,开机时间能快才怪。

今天我们就来聊聊,怎么通过裁剪内核模块和驱动,让PDA的启动速度提升一个档次。我个人习惯把这一步叫做「内核瘦身」,说白了就是给内核做减法。

3.1 为什么要去掉不必要的模块?

先看一组数据。我在一个实际项目中做过测试:

优化项 优化前 优化后 节省时间
内核镜像大小 4.2MB 2.8MB 33%
解压时间 320ms 210ms 110ms
驱动初始化时间 680ms 290ms 390ms
总启动时间 3.2s 2.4s 0.8s

看到了吗?光是去掉不必要的驱动,就能省下将近1秒的时间。对于PDA这种嵌入式设备来说,这1秒可能就是用户体验从「能接受」到「很流畅」的质变。

核心原则:内核启动时,每个模块的初始化都需要时间。哪怕这个模块最终没有被使用,它的初始化代码也会被执行一遍。所以,能编译进内核的,尽量少;能做成模块的,尽量晚加载

3.2 如何分析当前内核的模块加载情况?

动手之前,先得搞清楚当前系统到底加载了哪些模块。我常用的方法有这几种:

3.2.1 查看启动日志

启动日志里记录了每个模块的加载时间。用这个命令:

dmesg | grep "initcall" | sort -k4 -n

输出大概长这样:

[    0.123456] initcall i2c_init+0x0/0x80 returned 0 after 1234 usecs
[    0.234567] initcall spi_init+0x0/0x100 returned 0 after 2345 usecs
[    0.345678] initcall mmc_init+0x0/0x200 returned 0 after 3456 usecs

嗯,这里要注意:后面的usecs就是每个模块消耗的微秒数。把耗时最长的前20个模块列出来,你就知道该对谁下手了。

我的小技巧:我曾经在一个项目中,发现一个叫「leds-gpio」的模块居然花了50ms初始化。查了半天,发现这个模块对应的GPIO引脚根本没接LED。去掉之后,启动时间直接少了50ms。这种「幽灵模块」在嵌入式设备里特别常见。

3.2.2 查看当前加载的模块列表

lsmod | sort

这个命令会列出所有已加载的模块。对照你的硬件原理图,把那些「硬件上不存在」的模块标记出来。

3.3 具体裁剪方法

好了,分析完了,该动手了。我一般分三步走:

3.3.1 第一步:从内核配置中移除

进入内核源码目录,运行:

make menuconfig

然后逐项检查。我建议重点关注这几个地方:

  • Device Drivers —— 这里是最容易藏污纳垢的地方。什么显卡驱动、声卡驱动、网卡驱动,如果你的PDA没有这些硬件,统统去掉。
  • Networking support —— 很多PDA只需要简单的网络功能。像IPV6、蓝牙、WiFi的某些子选项,能关就关。
  • File systems —— 只保留你实际使用的文件系统。比如只用ext4,就把ext2、ext3、btrfs、xfs这些都去掉。
  • Multimedia support —— 摄像头、视频编解码器,没有硬件就别留着。

警告:千万不要为了追求极致速度而盲目裁剪。我曾经有一次把「Scheduler」相关的选项全关了,结果系统启动后调度器直接罢工,连shell都进不去。嗯,那次折腾了我一整天。

3.3.2 第二步:将驱动改为模块化加载

有些驱动虽然需要,但不必在启动时就加载。比如:

  • USB设备驱动 —— 用户插上U盘时才加载
  • 触摸屏校准驱动 —— 进入用户界面后再加载
  • 音频驱动 —— 播放声音时才加载

在menuconfig里,把对应的选项从 <*>(编译进内核)改为 <M>(编译为模块)。这样它们就不会在启动时初始化了。

3.3.3 第三步:使用内核启动参数禁用

有些驱动是内核强制编译进去的,没法通过menuconfig去掉。这时候可以用启动参数来禁用:

# 在bootargs中添加
modprobe.blacklist=module1,module2,module3

# 或者直接禁用整个子系统
acpi=off
noapic
nousb

举个例子,如果你的PDA没有USB Host功能,可以加:

nousb

这样内核就不会初始化USB子系统了,能省下大约100-200ms。

3.4 实战案例:一个PDA项目的瘦身过程

我记得去年帮一个客户优化他们的工业PDA。原始内核配置是从某个开发板移植过来的,里面塞了一大堆用不上的驱动。我们做了这么几件事:

  1. 去掉显卡驱动 —— 这个PDA用的是LCD屏,但配置里居然有HDMI、VGA、DisplayPort的驱动。去掉后内核小了300KB。
  2. 裁剪网络协议栈 —— 只需要TCP/IP和HTTP,把IPV6、IPSec、Netfilter全关了。启动时间少了150ms。
  3. 精简文件系统支持 —— 只保留ext4和FAT32。去掉NTFS、exFAT、JFFS2等。省了80ms。
  4. 禁用USB Gadget —— 这个PDA不需要作为USB设备连接电脑。去掉后省了60ms。

最终,启动时间从4.1秒降到了2.6秒。客户说:「这感觉像换了一台新设备。」

关键数据:每去掉一个不必要的模块,平均能节省5-20ms。如果你能去掉50个模块,那就是0.25-1秒的提升。这个收益非常可观。

3.5 验证与测试

裁剪完之后,一定要做两件事:

  • 功能测试 —— 确保所有硬件功能正常。特别是触摸屏、按键、显示这些核心功能。
  • 压力测试 —— 连续重启100次,看有没有偶发性的启动失败。

我曾经在一次优化中,把「RTC驱动」去掉了。结果设备每次断电重启后时间都归零,用户投诉说日志时间戳全乱了。嗯,这个教训让我记住了:有些驱动虽然启动时用不上,但运行时是必须的

3.6 总结

内核启动参数调优,说白了就是「知己知彼」。先搞清楚你的硬件需要什么,再对照内核配置一项一项地砍。这个过程很枯燥,但效果立竿见影。

下一章我们会聊更进阶的话题:内核压缩算法与解压速度的权衡。到时候我会分享一个让内核解压时间缩短40%的骚操作,敬请期待。

课后作业:打开你的PDA内核配置,用make menuconfig逐项检查,找出至少10个可以去掉的模块。然后重新编译内核,对比启动时间的变化。欢迎在评论区分享你的成果。