第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_PROC、CONFIG_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 加载配置
加载配置有两种方式:
- 直接复制:
cp config_my_project_v1 .config - 使用
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以内。