Linux驱动 2026年4月22日 45 分钟

menuconfig 与 Kconfig 详解

# Linux 内核配置系统 — menuconfig 与 Kconfig 详解 ## 目录 – [一、me…

# 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

“`

上一篇 GDB调试

本文将由浅入深讲解GDB调试使用

下一篇 printk 日志级别详解

级别 名称 值 说明 0 KERN_EMERG 0 系统崩溃,系统不可用 1 KERN_ALERT 1 需要立即处理的紧...