第4章 内核配置基础:配置菜单结构、关键配置项解析、保存与加载配置

好,咱们进入内核配置这个环节。说实话,很多做嵌入式开发的朋友,拿到QNX源码后第一反应就是——直接make。结果呢?编译出来的内核要么太大,要么跑不起来。我当年也踩过这个坑。

内核配置,说白了就是给QNX内核「量身定做」一套参数。你想想看,一个跑在128MB内存设备上的内核,跟一个跑在2GB内存设备上的内核,配置能一样吗?肯定不能。

4.1 配置菜单结构

QNX的内核配置,用的是类Linux的Kconfig体系。但别高兴太早,它跟Linux的menuconfig还是有区别的。我个人习惯用make menuconfig来配置,图形化界面,操作直观。

配置菜单的结构,大致分三层:

  • 顶层菜单:按功能模块划分,比如Process Manager、Memory Manager、Device Driver等
  • 子菜单:每个模块下的细分选项,比如Memory Manager下面有Page Size、Cache Policy等
  • 具体配置项:最底层的开关或数值,比如CONFIG_NUM_PROCCONFIG_STACK_SIZE

举个例子,你执行make menuconfig后,会看到类似这样的结构:

General setup  --->
    [*] Enable kernel debug
    [ ] Enable profiling
    (512) Maximum number of processes

Memory management  --->
    (4096) Page size (in bytes)
    [*] Enable MMU
    [ ] Enable swap

Device drivers  --->
    [*] Serial driver
    [ ] USB support
    [*] Ethernet driver

嗯,这里要注意:每个选项前面的括号含义不同。[*]表示编译进内核,[ ]表示不编译,()表示需要输入数值。

小技巧:我习惯在配置时先按/键搜索关键字,比如搜"MMU",能快速定位到相关配置项。这比一层层翻菜单快多了。

4.2 关键配置项解析

配置项那么多,哪些是必须关注的?我根据项目经验,挑几个最关键的说说。

4.2.1 进程管理相关

配置项 说明 典型值
CONFIG_NUM_PROC 最大进程数 256 / 512 / 1024
CONFIG_STACK_SIZE 内核栈大小(字节) 4096 / 8192
CONFIG_SCHED_PRIO 调度优先级级别数 32 / 64 / 256

我曾经在一个项目中,把CONFIG_NUM_PROC设成了64,结果系统运行到第65个进程时直接崩溃。排查了半天才发现是这里的问题。所以,这个值要根据实际业务场景来定,别拍脑袋。

4.2.2 内存管理相关

配置项 说明 典型值
CONFIG_PAGE_SIZE 内存页大小(字节) 4096 / 16384 / 65536
CONFIG_HEAP_SIZE 内核堆大小(字节) 65536 / 131072
CONFIG_MMU 是否启用MMU y / n

这里有个坑:页大小设得越大,TLB miss越少,但内存浪费也越多。我建议,如果你的设备内存小于64MB,用4KB页;大于256MB,可以考虑16KB或64KB。

警告CONFIG_MMU一旦设为n,系统将失去内存保护能力。除非你的设备是裸机级别的简单系统,否则千万别关。我曾经见过有人为了省那几KB内存关了MMU,结果一个野指针把整个系统搞挂了。

4.2.3 设备驱动相关

配置项 说明 典型值
CONFIG_SERIAL 串口驱动 y / n
CONFIG_ETH 以太网驱动 y / n
CONFIG_USB USB驱动 y / n / m

注意,这里的m表示编译成模块,可以在运行时动态加载。我一般建议,把不常用的驱动编译成模块,这样内核镜像能小不少。

4.3 保存与加载配置

配置完了,怎么保存?怎么复用?这里有几个方法。

4.3.1 保存配置

在menuconfig界面,按Exit退出时,系统会问你是否保存。选Yes,配置会写入.config文件。

我个人习惯,每次配置完都手动备份一份:

cp .config config_my_project_v1

为什么?因为有时候你改了配置,跑起来发现不对,想回退。没有备份的话,只能重新配一遍,那叫一个痛苦。

4.3.2 加载配置

加载配置有两种方式:

  1. 直接复制cp config_my_project_v1 .config
  2. 使用make defconfig:从默认配置加载

还有一种情况,你拿到别人的配置文件,但版本不同。这时候可以用make oldconfig,它会自动处理新增或删除的配置项。

make oldconfig < config_other_version

嗯,这里要注意:oldconfig会交互式询问你新增的配置项怎么设。如果你不想交互,可以用make silentoldconfig,它会自动采用默认值。

核心要点:配置文件的本质是一个键值对列表。你可以手动编辑.config文件,但我不建议这么做。因为格式稍微写错一点,整个配置就废了。还是用menuconfig靠谱。

4.4 实战:一个典型的裁剪配置流程

说了这么多,咱们来走一遍实际流程。假设你有一个设备,内存64MB,只需要串口和以太网功能。

第一步,加载基础配置:

make x86_64_defconfig   # 假设是x86平台

第二步,进入menuconfig:

make menuconfig

第三步,关闭不需要的功能:

  • 关掉USB支持:Device drivers -> [ ] USB support
  • 关掉音频驱动:Device drivers -> [ ] Audio driver
  • 关掉图形支持:Device drivers -> [ ] Framebuffer

第四步,调整内存参数:

  • 页大小设为4KB:Memory management -> (4096) Page size
  • 最大进程数设为128:General setup -> (128) Maximum number of processes

第五步,保存并退出。

第六步,编译:

make -j4

编译完成后,看看内核镜像大小。我做过测试,一个全功能的内核大概2MB左右,经过裁剪后能降到500KB以内。对于64MB内存的设备来说,这省下来的1.5MB空间,可以多跑好几个应用了。

经验之谈:裁剪配置不是一蹴而就的。我建议先做一个最小可用配置,然后逐步添加功能。每加一个功能,就编译测试一次。这样出了问题,能很快定位到是哪个配置项引起的。

好了,内核配置的基础就这些。下一章咱们聊聊如何通过配置来优化内核性能。到时候我会分享一个真实案例——怎么把一个跑在500ms的启动时间,优化到200ms以内。