# Linux 内核配置系统 — menuconfig 与 Kconfig 详解
## 目录
– [一、menuconfig 概述](#一menuconfig-概述)
– [二、Kconfig 语法详解](#二kconfig-语法详解)
– [三、在 menuconfig 中添加选项](#三在-menuconfig-中添加选项)
– [四、将模块编译进内核](#四将模块编译进内核)
– [五、查看驱动是否编译进内核](#五查看驱动是否编译进内核)
– [六、各种 config 文件的区别](#六各种-config-文件的区别)
– [七、常见问题](#七常见问题)
—
## 一、menuconfig 概述
`menuconfig` 是 Linux 内核提供的**图形化配置工具**,基于 NCurses 文本图形库,在终端中通过方向键和回车进行配置选择。
### 启动方式
“`bash
cd /home/eh/linux/xuexi1/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek
# 方式1:启动 menuconfig(需要先有 .config 文件)
make menuconfig
# 方式2:从默认配置开始
make imx_v7_defconfig
make menuconfig
“`
### 界面符号说明
“`
Linux Kernel Configuration
————————-
[*] 64-bit kernel # [*] = 内置(Y)
[ ] Enable loadable module support # [ ] = 不选(N)
<M> NTFS Read-Write file system support # <M> = 模块(M)
— Some random driver # — = 不可配置(仅标题)
> Device Drivers # > = 子菜单入口
(1024) Maximum number of CPUs # ( ) = 数字输入
[*) Power management —> # [*] —> = 选中且有子菜单
“`
| 符号 | 含义 | .config 值 |
|——|——|————|
| `[*]` | 选中,构建进内核 | `=y` |
| `[ ]` | 未选中 | `=n` |
| `<M>` | 编译为模块 | `=m` |
| `< >` | 模块化但未选 | 无定义 |
| `(value)` | 数字输入 | `=1024` |
| `”string”` | 字符串输入 | `=”value”` |
| `>xxx` | 跳转到子菜单 | – |
| `—` | 仅标题,不可选 | – |
### 三种编译方式
| 方式 | 菜单符号 | .config 值 | 效果 |
|——|———|———–|——|
| 写入内核 | `[*]` | `=y` | `.o` 链接进 `vmlinux` / `zImage` |
| 编译模块 | `<M>` | `=m` | 编译为 `.ko` 可加载模块 |
| 不编译 | 留空 | `=n` | 不编译,代码不包含在镜像中 |
### 常用操作键
| 按键 | 功能 |
|——|——|
| 方向键 | 移动选择 |
| 回车 | 确认 / 进入子菜单 |
| `Y` | 选中,构建进内核 (`=y`) |
| `N` | 取消选中 (`=n`) |
| `M` | 编译为模块 (`=m`) |
| `Esc Esc` | 返回上级菜单 |
| `/` | 搜索配置选项 |
| `?` | 查看当前选项帮助 |
| `Tab` | 在底部按钮间切换 |
—
## 二、Kconfig 语法详解
`Kconfig` 是 `menuconfig` 的配置描述文件,内核源码中每个目录都有 `Kconfig`,定义该目录下各项配置的含义和依赖关系。
### 基本结构
“`kconfig
config HELLO
tristate “Hello driver support”
depends on ARCH_ARM
help
This is a simple hello world driver.
“`
– `config XXX` — 定义配置项名称,生成 `CONFIG_XXX` 宏
– `tristate` — 支持三种状态(y/n/m),普通 bool 只支持 y/n
– 字符串 — 菜单中显示的描述文字
### Kconfig 关键字速查
| 关键字 | 作用 |
|——–|——|
| `config` | 定义配置项,生成 `CONFIG_XXX` 宏 |
| `tristate` | 三态选项(y/n/m),用于可加载模块 |
| `bool` | 布尔选项(y/n),无模块选项 |
| `string` | 字符串输入 |
| `int` | 整数输入 |
| `hex` | 十六进制输入 |
| `depends on` | 依赖其他配置项,必须先选中依赖项才能选此项 |
| `select` | 反向依赖,自动选中目标项 |
| `imply` | 软依赖,选中此项时默认选中目标项(可手动取消) |
| `help` / `—help—` | 帮助文档 |
| `menu` / `endmenu` | 创建子菜单 |
| `choice` / `endchoice` | 创建单选组 |
| `if` / `endif` | 条件组 |
| `source` | 引用其他 Kconfig 文件 |
### 逐行语法解析
“`kconfig
# 注释,以 # 开头
menu “Driver Configuration” # 创建子菜单
endmenu # 结束子菜单
config HELLO_MODULE
bool “Hello module” # 布尔类型,选中=y,未选中=n
help
Enable hello module.
config HELLO_TRISTATE
tristate “Hello module (loadable)” # 三态,支持 y/n/m
default y # 默认值
depends on ARCH_ARM # 依赖项,ARM 架构才显示此选项
select MISC_DEVICES # 选中此项时,自动选中 MISC_DEVICES
imply SYSFS # 软依赖,默认选中但可取消
help
This is the help text.
“`
### depends on / select / imply 的区别
| 关键字 | 效果 | 可否手动取消 |
|——–|——|————|
| `depends on A` | 必须先选中 A 才能选此项 | – |
| `select B` | 选中此项时,强制选中 B | 不能取消 |
| `imply B` | 选中此项时,默认选中 B | 可以取消 |
### menu 子菜单示例
“`kconfig
menu “My Device Drivers”
depends on ARCH_MXC
config MY_HELLO
tristate “Hello driver”
default y
help
A simple hello driver.
config MY_HELLO_DEBUG
bool “Debug output”
depends on MY_HELLO
default n
endmenu
“`
### choice 单选组示例
“`kconfig
choice
prompt “CPU frequency scaling governor”
default CPU_FREQ_DEFAULT_GOV_ONDEMAND
config CPU_FREQ_DEFAULT_GOV_PERFORMANCE
bool “performance”
help
Run the CPU at maximum frequency.
config CPU_FREQ_DEFAULT_GOV_ONDEMAND
bool “ondemand”
help
Run the CPU at max frequency when needed.
endchoice
“`
### source 引用其他 Kconfig
“`kconfig
# 在 drivers/char/Kconfig 中添加
source “drivers/my_driver/Kconfig”
“`
—
## 三、在 menuconfig 中添加选项
### 完整步骤
#### 步骤1:创建或修改 Kconfig
在驱动所在目录的 `Kconfig` 中添加:
“`kconfig
menu “My Driver Configuration”
depends on ARCH_MXC || ARCH_ARM
config MY_HELLO
tristate “Hello driver support”
default y
help
A simple hello world character device driver.
This driver creates a /dev/hello device node.
config MY_HELLO_VERBOSE
bool “Enable verbose output”
depends on MY_HELLO
default n
help
Enable debug printk messages.
endmenu
“`
在父目录的 Kconfig 中引用:
“`kconfig
# 在 drivers/char/Kconfig 中添加
source “drivers/my_driver/Kconfig”
“`
#### 步骤2:修改驱动 Makefile
“`makefile
obj-$(CONFIG_MY_HELLO) += hello.o
# 多文件模块
obj-$(CONFIG_MY_HELLO) += hello_core.o
obj-$(CONFIG_MY_HELLO_VERBOSE) += hello_debug.o
“`
展开说明:
– `CONFIG_MY_HELLO=y` → `obj-y += hello.o` → 编译进内核
– `CONFIG_MY_HELLO=m` → `obj-m += hello.o` → 编译为模块
– `CONFIG_MY_HELLO=n` → 不生成任何编译指令 → 不编译
#### 步骤3:驱动代码中的条件编译
“`c
#include <linux/module.h>
#include <linux/kernel.h>
#ifdef CONFIG_MY_HELLO_VERBOSE
#define DPRINTK(fmt, args…) printk(KERN_DEBUG “[HELLO] ” fmt, ##args)
#else
#define DPRINTK(fmt, args…) do {} while(0)
#endif
#ifdef CONFIG_MY_HELLO
static int __init hello_init(void)
{
printk(KERN_INFO “Hello driver initialized\n”);
DPRINTK(“Verbose: module init\n”);
return 0;
}
static void __exit hello_exit(void)
{
printk(KERN_INFO “Hello driver removed\n”);
}
module_init(hello_init);
module_exit(hello_exit);
#endif
MODULE_LICENSE(“GPL”);
MODULE_AUTHOR(“ErHu”);
MODULE_DESCRIPTION(“Hello driver for IMX6ULL”);
“`
#### 步骤4:刷新配置,重新编译
“`bash
make menuconfig
# Device Drivers -> My Driver Configuration -> 选中 Hello driver support
make
“`
### 目录结构示例
“`
drivers/char/my_driver/
├── Kconfig # 配置选项定义
├── Makefile # 编译规则
└── hello.c # 驱动源码
“`
—
## 四、将模块编译进内核
### 方法一:通过 menuconfig(最常用)
“`bash
make menuconfig
# Device Drivers ──>
# Character devices ──>
# <*> IMX6ULL Hello Driver # Y 键选中,写入内核
“`
### 方法二:直接修改 .config 文件
“`bash
vi .config
# 编译进内核
CONFIG_MY_HELLO=y
# 编译为模块
CONFIG_MY_HELLO=m
# 不编译
# CONFIG_MY_HELLO is not set
“`
### 方法三:用 sed 命令行修改
“`bash
# 编译进内核
sed -i ‘s/# CONFIG_MY_HELLO is not set/CONFIG_MY_HELLO=y/’ .config
# 编译为模块
sed -i ‘s/CONFIG_MY_HELLO=y/CONFIG_MY_HELLO=m/’ .config
# 禁用
sed -i ‘/CONFIG_MY_HELLO/d’ .config
# 追加新选项
echo “CONFIG_MY_HELLO=y” >> .config
“`
### 方法四:通过 defconfig
“`bash
# 查看现有配置文件
ls arch/arm/configs/
# 基于某个配置文件修改
cp arch/arm/configs/imx_v7_defconfig .config
echo “CONFIG_MY_HELLO=y” >> .config
make olddefconfig # 解析依赖,更新 .config
“`
### IMX6ULL 相关配置
“`bash
# 查看 IMX6ULL 架构配置
grep -i “IMX” .config | head -20
# 查看设备树相关配置
grep “CONFIG_ARCH_MXC” .config
grep “CONFIG_SOC_IMX6ULL” .config
“`
—
## 五、查看驱动是否编译进内核
### 方法一:查看 .config 文件
“`bash
grep “CONFIG_MY_HELLO” .config
# 输出示例:
CONFIG_MY_HELLO=y # 编译进内核
CONFIG_MY_HELLO=m # 编译为模块
# CONFIG_MY_HELLO is not set # 未启用
“`
### 方法二:在 menuconfig 中搜索
“`bash
make menuconfig
# 按 / 键,输入 MY_HELLO 搜索
“`
会显示:
“`
Symbol: MY_HELLO [=y]
Type : tristate
Defined at drivers/my_driver/Kconfig:3
Prompt: Hello driver support
Location:
-> Device Drivers
-> My Driver Configuration
Selected: [ ]
Depends: <none>
“`
### 方法三:查看编译产物
“`bash
# 编译进内核时(=y),会生成目标文件
ls drivers/my_driver/*.o
# 编译为模块时(=m),会生成 .ko
ls drivers/my_driver/*.ko
# 查看 vmlinux 中是否包含符号
nm vmlinux | grep hello
“`
### 方法四:在开发板上查看
“`bash
# 查看已加载的模块
lsmod
# 查看模块详细信息
modinfo hello.ko
# 搜索内核符号表
cat /proc/kallsyms | grep hello
# 查看设备节点
ls /dev/hello
“`
### 方法五:通过 /proc/config.gz(如果内核开启了 CONFIG_IKCONFIG)
“`bash
# 在开发板上
zcat /proc/config.gz | grep MY_HELLO
“`
—
## 六、各种 config 文件的区别
### 文件一览
| 文件 | 位置 | 作用 | 生成方式 |
|——|——|——|———|
| `.config` | 内核源码根目录 | 实际生效的配置,当前编译使用的配置 | `make menuconfig` / `make defconfig` |
| `defconfig` | `arch/arm/configs/*.defconfig` | 预定义的默认配置集合 | 从 `.config` 导出 `make savedefconfig` |
| `allyesconfig` | – | 所有选项都设为 y | `make allyesconfig` |
| `allnoconfig` | – | 所有选项都设为 n(最小配置) | `make allnoconfig` |
| `tinyconfig` | – | 最小化配置 | `make tinyconfig` |
| `.config.old` | 内核源码根目录 | 上一次配置的备份 | `make menuconfig` 自动生成 |
### .config 的生成过程
“`
arch/arm/configs/imx_v7_defconfig (预定义配置)
│
│ make imx_v7_defconfig
▼
.config (首次生成)
│
│ make menuconfig
▼
.config (用户修改后的配置)
│
│ make oldconfig
▼
完整的 .config(依赖解析后)
“`
### allyesconfig / allnoconfig 用途
“`bash
# 开启所有选项(=y),用于测试最大配置
make allyesconfig
# 关闭所有可选选项(=n),用于测试最小配置
make allnoconfig
“`
### defconfig 和 .config 的关系
– `defconfig` 是**预置模板**,只包含用户可能关心的选项,未列出的默认 `=n`
– `.config` 是**当前生效配置**,包含所有选项及其当前值
“`bash
# 从 .config 导出 defconfig(保存当前配置为模板)
make savedefconfig
# 生成 defconfig 文件,只包含与默认值不同的选项
# 基于现有 defconfig 创建 .config
make imx_v7_defconfig
# 等价于:cp arch/arm/configs/imx_v7_defconfig .config
“`
### .config.old 的作用
每次运行 `make menuconfig` 保存退出时,会自动备份:
“`
.config ← 当前配置
.config.old ← 上一次保存的配置
“`
可用 `make oldconfig` 从 `.config.old` 恢复。
—
## 七、常见问题
### Q1:选项是灰色不可选
原因:`depends on` 的依赖项没有选中。
解决:先选中依赖项(如 `ARCH_MXC`)。
### Q2:按 M 键没用,还是显示 `< >`
原因:该项是 `bool` 类型,不支持模块。
解决:改 `bool` 为 `tristate`。
### Q3:make menuconfig 报错 missing ncurses
“`bash
sudo apt install libncurses-dev
“`
### Q4:修改了 Kconfig 但 menuconfig 中看不到
解决:确保在正确的内核源码目录中操作,执行 `make menuconfig` 会重新读取所有 Kconfig。
### Q5:内核版本不匹配
“`bash
# 开发板上查看内核版本
uname -r
# 虚拟机中查看内核版本
cat /home/eh/linux/xuexi1/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek/.config | grep VERSION
“`
### Q6:模块加载失败
可能原因:
1. 内核版本不匹配
2. 模块架构不匹配(ARM vs x86)
3. 交叉编译工具链错误
4. NFS 根文件系统未正确挂载
### Q7:如何查看某个选项的所有依赖链
“`bash
make menuconfig
# 按 / 搜索
# 找到 Depends: 项,按路径层层查找
“`
—
## 八、最佳实践
### 开发驱动时的标准流程
“`bash
# 1. 确保有默认配置
cp arch/arm/configs/imx_v7_defconfig .config
# 2. 打开 menuconfig 添加你的驱动选项
make menuconfig
# Device Drivers -> My Driver Configuration -> 选中
# 3. 编译内核和模块
make -j4
# 4. 编译并安装模块
make modules_install
# 5. 更新开发板上的文件系统或 NFS
# 如果使用 NFS,直接在 /home/eh/linux/nfs/xuexi/rootfs 下就有了
“`
### Makefile 中的 CONFIG 判断
“`makefile
# 基础写法
obj-$(CONFIG_MY_HELLO) += hello.o
# 多条件判断
obj-$(CONFIG_MY_HELLO) += hello.o
obj-$(CONFIG_MY_HELLO_DEBUG) += hello_debug.o
# 子目录递归
obj-$(CONFIG_MY_HELLO) += subdir/
# 子目录的 Makefile
obj-$(CONFIG_MY_HELLO) += hello_core.o
obj-$(CONFIG_MY_HELLO) += hello_io.o
“`