4. 内核配置与编译:使用 make menuconfig 进行内核配置、配置选项详解、编译内核与模块

内核配置与编译是嵌入式 Linux 驱动开发的核心环节。开发者需要根据目标硬件平台裁剪内核功能,选择必要的驱动模块,并最终生成可引导的内核镜像与可加载模块。本章将详细讲解 make menuconfig 的使用方法、关键配置选项的含义,以及完整的编译流程。

4.1 内核配置系统概述

Linux 内核采用 Kconfig / Kbuild 双系统管理配置与编译。Kconfig 定义配置选项的层次结构与依赖关系,Kbuild 根据 .config 文件决定哪些源文件参与编译。

核心概念: 内核配置的本质是生成 .config 文件,该文件包含约 10,000 个配置选项的 Y/N/M 状态。Y 表示编译进内核,M 表示编译为可加载模块,N 表示不编译。
内核配置与编译流程 Kconfig 文件 make menuconfig .config 文件 Kbuild 系统 zImage / uImage *.ko 模块文件 System.map / dtbs 配置选项 Y=编译进内核 M=编译为模块 N=不编译 make 生成内核镜像,make modules 生成 .ko 模块文件

4.2 使用 make menuconfig 进行内核配置

make menuconfig 是基于 ncurses 库的文本界面配置工具,是嵌入式开发中最常用的配置方式。它提供树形菜单、搜索功能和实时依赖检查。

4.2.1 启动配置界面

进入内核源码根目录后执行以下命令:

# 确保已安装 ncurses 开发库
sudo apt-get install libncurses5-dev

# 加载默认配置(以 ARM 为例)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- defconfig

# 启动菜单配置
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
提示: 首次配置建议先执行 defconfig 生成基础配置,再通过 menuconfig 进行裁剪。直接执行 make menuconfig 会使用当前目录下已有的 .config 文件,若不存在则使用默认配置。

4.2.2 界面操作指南

按键 功能 说明
↑ ↓ 上下移动光标 在菜单项之间导航
Enter 进入子菜单 / 确认 对 [ ] 或 < > 项按 Enter 进入子菜单
Y / N / M 设置选项状态 Y=编译进内核,N=不编译,M=编译为模块
Space 循环切换状态 在 Y → M → N 之间循环
/ 搜索选项 按名称或描述搜索配置项,支持通配符
? 查看帮助 显示选项的详细说明、依赖关系和反向依赖
Esc Esc 返回上级 / 退出 连续按两次 Esc 返回上一级菜单

4.2.3 配置选项的三种状态

在 menuconfig 界面中,选项前缀符号表示其当前状态:

  • [ ] — 选项未选中(N),对应 # CONFIG_XXX is not set
  • [Y] — 选项编译进内核(Y),对应 CONFIG_XXX=y
  • <M> — 选项编译为模块(M),对应 CONFIG_XXX=m
  • < > — 选项可配置为模块但当前未选中
注意: 并非所有选项都支持 M 状态。驱动选项通常支持 Y/M/N,而核心调度、内存管理等基础功能只支持 Y/N。如果某个选项的依赖项未满足,该选项会显示为灰色不可选。

4.3 关键配置选项详解

嵌入式 Linux 内核配置涉及数千个选项,以下表格列出与驱动开发最相关的核心配置区域:

配置路径 配置项 典型值 说明
General setup Cross-compiler toolchain arm-linux-gnueabihf- 指定交叉编译工具链前缀
Enable loadable module support CONFIG_MODULES Y 必须启用,否则无法加载模块
Enable loadable module support CONFIG_MODULE_UNLOAD Y 允许卸载模块,调试时建议开启
Enable loadable module support CONFIG_MODVERSIONS N 模块版本校验,开发阶段建议关闭
Device Drivers CONFIG_GPIO_SYSFS Y GPIO 用户空间访问接口
Device Drivers → Character devices CONFIG_DEVMEM Y 允许通过 /dev/mem 访问物理内存
Device Drivers → I2C support CONFIG_I2C Y I2C 总线驱动支持
Device Drivers → SPI support CONFIG_SPI Y SPI 总线驱动支持
Device Drivers → Input device support CONFIG_INPUT Y 输入子系统支持
File systems CONFIG_EXT4_FS Y 根文件系统常用格式
File systems → Pseudo filesystems CONFIG_DEBUG_FS Y 调试文件系统,驱动调试必备

4.4 编译内核与模块

配置完成后,进入编译阶段。嵌入式开发通常使用交叉编译,需要指定架构和工具链。

4.4.1 编译完整内核

# 设置环境变量(也可在 make 命令行指定)
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-

# 编译内核镜像与设备树
make -j4 zImage dtbs

# 编译所有模块
make -j4 modules

# 安装模块到目标文件系统目录
make INSTALL_MOD_PATH=/path/to/rootfs modules_install
关键参数说明:
-j4:并行编译,数字为 CPU 核心数 + 1 或 2 倍核心数。
zImage:ARM 平台常用压缩内核镜像格式。
dtbs:编译设备树二进制文件(.dtb)。
INSTALL_MOD_PATH:指定模块安装路径,通常指向目标根文件系统。

4.4.2 仅编译内核模块

当只修改了驱动代码时,无需重新编译整个内核,可以单独编译模块:

# 编译所有模块
make -j4 modules

# 编译指定目录下的模块
make -j4 M=drivers/char

# 编译单个模块文件
make -j4 drivers/char/my_driver.ko

4.4.3 编译输出产物

产物 路径 说明
zImage arch/arm/boot/zImage 压缩后的内核镜像,用于引导
uImage arch/arm/boot/uImage U-Boot 格式的内核镜像(需额外打包)
*.dtb arch/arm/boot/dts/*.dtb 设备树二进制文件,描述硬件拓扑
*.ko 各驱动目录下 可加载内核模块文件
System.map 源码根目录 内核符号表,调试和模块加载时使用
modules.order 源码根目录 模块加载顺序文件

4.5 配置与编译常见问题

  • 配置丢失: 执行 make distclean 会删除 .config 文件,建议备份 .config 为 config_xxx。
  • 依赖未满足: 配置时若选项灰色,按 ? 查看依赖链,先启用依赖项。
  • 模块版本不匹配: 加载模块时出现 Invalid module format,检查内核版本与模块编译时的版本是否一致。
  • 编译错误: 交叉编译工具链版本过旧或过新可能导致编译失败,建议使用内核文档推荐的工具链版本。
  • 模块无法加载: 使用 modinfo 查看模块信息,insmod 失败时用 dmesg 查看内核日志。
内核模块编译与加载流程 编写驱动源码 .c 编写 Kconfig / Makefile make modules 生成 .ko 文件 scp 到目标板 insmod / modprobe 调试技巧:使用 dmesg 查看加载日志,lsmod 列出已加载模块,rmmod 卸载模块 modprobe 自动处理模块依赖,insmod 需指定完整路径

4.6 配置裁剪实战建议

嵌入式系统资源有限,内核配置裁剪至关重要。以下是一套经过验证的裁剪策略:

  1. 从 defconfig 起步: 使用目标平台的标准默认配置作为基础。
  2. 禁用未使用的硬件驱动: 在 Device Drivers 中关闭目标板上不存在的硬件驱动。
  3. 精简文件系统支持: 只保留根文件系统使用的格式(如 ext4、squashfs)。
  4. 关闭调试选项: 发布版本关闭 CONFIG_DEBUG_KERNEL 及相关调试选项。
  5. 使用内核尺寸分析工具: make kernelrelease 查看版本,size 命令分析镜像大小。
经验值: 一个典型的嵌入式 Linux 内核(ARM Cortex-A7,256MB RAM)裁剪后,zImage 大小可控制在 3~5MB,模块总大小约 10~20MB。每减少 1MB 内核大小,可节省约 2~4MB 运行时内存。