未分类 2026年4月22日 28 分钟

将驱动模块编译进内核的完整方法

# 将驱动模块编译进内核的完整方法 ## 目录 – [方法一:外部模块编译](#方法一外部模块编译最灵活) …

# 将驱动模块编译进内核的完整方法

## 目录

– [方法一:外部模块编译](#方法一外部模块编译最灵活)

– [方法二:内置模块编译](#方法二内置模块编译编译进内核镜像)

– [方法三:通过 menuconfig 图形界面](#方法三通过-menuconfig-图形界面配置)

– [方法四:直接修改 .config 文件](#方法四直接修改-config-文件)

– [方法五:通过 defconfig 预置配置](#方法五通过-defconfig-预置配置)

– [方法六:通过 Kbuild 条件编译](#方法六通过-kbuild-条件编译不修改-kconfig)

– [方法七:通过 modprobe 自动加载](#方法七通过-modprobe-自动加载)

– [方法八:通过内核引导参数加载](#方法八通过内核引导参数加载)

– [总结对比](#总结对比)

– [开发阶段推荐流程](#开发阶段推荐流程)

## 方法一:外部模块编译(最灵活)

驱动代码放在内核源码**外部**,单独 `make` 编译,生成 `.ko` 文件。

### 目录结构

“`

/home/eh/linux/xuexi1/my_driver/01char_driver/驱动环境测试/

├── moudle.c

└── Makefile

“`

### Makefile

“`makefile

export ARCH=arm

export CROSS_COMPILE=arm-linux-gnueabihf-

obj-m +=moudle.o

KDIR := /home/eh/linux/xuexi1/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek

PWD := $(shell pwd)

modules:

    $(MAKE) -C $(KDIR) M=$(PWD) modules

clean:

    $(MAKE) -C $(KDIR) M=$(PWD) clean

“`

### 编译和加载

“`bash

make                          # 编译 .ko

insmod moudle.ko             # 加载

rmmod moudle                 # 卸载

“`

### 适用场景

驱动开发调试阶段,改代码后快速重新编译测试。

## 方法二:内置模块编译(编译进内核镜像)

驱动代码放在内核源码**内部**,编译时链接进 `zImage`。

### 步骤 1:创建内核源码中的驱动目录

“`bash

mkdir -p /home/eh/linux/xuexi1/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek/drivers/char/my_driver

“`

### 步骤 2:复制驱动源码

“`bash

cp /home/eh/linux/xuexi1/my_driver/01char_driver/驱动环境测试/moudle.c \

   /home/eh/linux/xuexi1/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek/drivers/char/my_driver/

“`

### 步骤 3:创建 Kconfig

“`bash

vi /home/eh/linux/xuexi1/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek/drivers/char/my_driver/Kconfig

“`

“`kconfig

config MY_HELLO

    tristate “Hello driver for IMX6ULL”

    default y

    help

      A simple hello world character device driver.

“`

### 步骤 4:创建 Makefile

“`bash

vi /home/eh/linux/xuexi1/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek/drivers/char/my_driver/Makefile

“`

“`makefile

obj-$(CONFIG_MY_HELLO) += hello.o

“`

### 步骤 5:在父级 Makefile 中引用

“`bash

vi /home/eh/linux/xuexi1/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek/drivers/char/Makefile

“`

在文件末尾添加:

“`makefile

obj-$(CONFIG_MY_HELLO) += my_driver/

“`

### 步骤 6:在父级 Kconfig 中引用

“`bash

vi /home/eh/linux/xuexi1/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek/drivers/char/Kconfig

“`

找到合适的位置添加:

“`kconfig

source “drivers/char/my_driver/Kconfig”

“`

### 步骤 7:配置并编译

“`bash

cd /home/eh/linux/xuexi1/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek

# 方式A:通过 menuconfig

make menuconfig

# 路径:Device Drivers -> Character devices -> Hello driver for IMX6ULL

# 方式B:直接修改 .config

echo “CONFIG_MY_HELLO=y” >> .config

make -j4

“`

### 验证

“`bash

# 编译后,内核镜像中会包含驱动

ls drivers/char/my_driver/*.o

# 内核镜像变大

ls -lh vmlinux

“`

### 适用场景

驱动开发完成,正式集成到内核中。

## 方法三:通过 menuconfig 图形界面配置

### 启动 menuconfig

“`bash

cd /home/eh/linux/xuexi1/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek

make menuconfig

“`

### 操作步骤

1. 按 `/` 键,输入 `MY_HELLO`,搜索配置项位置

2. 记住路径:`Location: -> Device Drivers -> Character devices -> My Driver`

3. 用方向键导航到该位置

4. 按 `Y` 键 — 编译进内核(`[*]`)

5. 或按 `M` 键 — 编译为模块(`<M>`)

6. 保存退出:`Esc Esc` → `Yes`

### 验证配置

“`bash

grep “CONFIG_MY_HELLO” .config

# 输出应为:CONFIG_MY_HELLO=y  或  CONFIG_MY_HELLO=m

“`

## 方法四:直接修改 .config 文件

### 添加配置项

“`bash

# 编辑 .config

vi /home/eh/linux/xuexi1/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek/.config

“`

在文件末尾添加:

“`

CONFIG_MY_HELLO=y

“`

### 用 sed 命令行修改

“`bash

# 编译进内核 (=y)

sed -i ‘/# CONFIG_MY_HELLO is not set/s/^# //’ .config

sed -i ‘s/CONFIG_MY_HELLO=m/CONFIG_MY_HELLO=y/’ .config

# 编译为模块 (=m)

sed -i ‘s/CONFIG_MY_HELLO=y/CONFIG_MY_HELLO=m/’ .config

# 禁用 (=n)

sed -i ‘/CONFIG_MY_HELLO/d’ .config

“`

### 追加新配置

“`bash

# 如果 .config 中没有 CONFIG_MY_HELLO

echo “CONFIG_MY_HELLO=y” >> .config

# 更新依赖

make oldconfig

“`

## 方法五:通过 defconfig 预置配置

### 从现有 defconfig 修改

“`bash

cd /home/eh/linux/xuexi1/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek

# 基于 IMX6ULL 默认配置

make imx_v7_defconfig

# 添加自定义驱动配置

echo “CONFIG_MY_HELLO=y” >> .config

# 更新配置(解析依赖)

make oldconfig

# 编译

make -j4

“`

### 导出当前配置为 defconfig

“`bash

# 把当前 .config 导出为 defconfig(精简格式,只保留非默认选项)

make savedefconfig

# 生成的文件

mv defconfig arch/arm/configs/imx_v7_mydefconfig

“`

## 方法六:通过 Kbuild 条件编译(不修改 Kconfig)

有时候只需要根据现有配置决定是否编译,不需要新增配置项。

### 方式 A:依赖已有配置项

“`makefile

# 假设 IMX6ULL 已经有 CONFIG_SOC_IMX6ULL

obj-$(CONFIG_SOC_IMX6ULL) += hello.o

“`

### 方式 B:根据架构编译

“`makefile

obj-$(CONFIG_ARCH_MXC) += hello.o

“`

### 方式 C:使用 ifdef 条件编译

“`makefile

ifdef CONFIG_MY_HELLO

obj-y += hello.o

endif

“`

### 方式 D:根据模块参数

“`makefile

obj-$(CONFIG_HELLO_ENABLE) += hello.o

“`

## 方法七:通过 modprobe 自动加载

如果驱动是 `.ko` 模块,可以让内核自动加载。

### 在 /etc/modules 中注册

“`bash

# 开发板上执行

echo “moudle” >> /etc/modules

“`

### 创建模块依赖配置

“`bash

# 更新模块依赖

depmod -a

# 查看依赖关系

cat /lib/modules/4.1.15/modules.dep | grep moudle

“`

### systemd 自动加载

“`bash

# 创建 systemd 服务

vi /etc/systemd/system/hello.service

“`

“`ini

[Unit]

Description=Hello Driver

After=basic.target

[Service]

Type=oneshot

ExecStart=/sbin/insmod /path/to/moudle.ko

RemainAfterExit=yes

[Install]

WantedBy=multi-user.target

“`

“`bash

systemctl enable hello

systemctl start hello

“`

## 方法八:通过内核引导参数加载

在 U-Boot 或内核启动参数中指定模块路径:

“`bash

# U-Boot 中设置

setenv bootargs “root=/dev/nfs nfsroot=… modules=module_name”

“`

或在开发板启动后:

“`bash

# 自动从指定目录加载所有模块

modprobe -a moudle

“`

## 总结对比

| 方法 | 配置方式 | 编译位置 | 是否修改内核源码 | 适用场景 |

|——|———|———|—————|———|

| 外部模块 | 外部 Makefile | 外部目录 | 不修改 | 开发调试 |

| 内置模块 | Kconfig + Makefile | 内核源码内 | 修改 | 正式集成 |

| menuconfig | 图形界面 | – | 不修改源码 | 快速配置 |

| 直接改 .config | 文本编辑器 | – | 不修改源码 | 快速测试 |

| defconfig | 预置配置 | – | 不修改源码 | 批量部署 |

| 条件编译 | 已有配置项 | 内核 | 不修改源码 | 复用配置 |

| modprobe 自动 | /etc/modules | 外部目录 | 不修改 | 自动加载 |

## 开发阶段推荐流程

“`

开发阶段:外部模块(方法一)

    ↓ 代码稳定后

集成阶段:内置模块(方法二)+ menuconfig(方法三)

    ↓ 配置确定后

发布阶段:defconfig(方法五)固化配置

“`

## 补充:驱动 Makefile 中的 CONFIG 判断

在 Makefile 中,`CONFIG_XXX` 会被替换为实际的值(y/m/n):

“`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

“`

展开说明:

– `CONFIG_MY_HELLO=y` → `obj-y += hello.o` → 编译进内核

– `CONFIG_MY_HELLO=m` → `obj-m += hello.o` → 编译为模块

– `CONFIG_MY_HELLO=n` → 不生成任何编译指令 → 不编译

## 补充:驱动代码中的条件编译

“`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”);

“`

上一篇 printk 日志级别详解

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

下一篇 Vim 编辑器详细使用手册

# Vim 编辑器详细使用手册 ## 目录 1. [Vim 简介](#1-vim-简介) 2. [三种模式](#2-三种...