Linux驱动编译与加载

一、内核源码树

bash 复制代码
Linux-4.9.88/
├── Makefile          # 顶层 Makefile,内核编译入口
├── Kconfig           # 顶层配置菜单入口
├── .config           # 保存 menuconfig 的选择结果
│
├── arch/             # 和 CPU 架构相关的代码
│   └── arm/          # ARM 架构相关代码
│       ├── configs/  # 默认配置文件
│       └── boot/
│           └── dts/  # 设备树文件
│
├── drivers/          # 各种设备驱动
│   ├── char/         # 字符设备驱动
│   ├── gpio/         # GPIO 驱动
│   ├── i2c/          # I2C 驱动
│   ├── spi/          # SPI 驱动
│   ├── net/          # 网络设备驱动
│   └── leds/         # LED 子系统驱动
│
├── include/          # 内核头文件
│   └── linux/        # 驱动常用头文件
│
├── fs/               # 文件系统
├── kernel/           # 内核核心代码
├── mm/               # 内存管理
├── net/              # 网络协议栈
└── scripts/          # 编译脚本和配置工具

写 LED 字符设备驱动时,需要关注的目录树:

arduino 复制代码
Linux-4.9.88/
├── Makefile
├── .config
├── drivers/
│   └── char/
│       ├── Makefile
│       ├── Kconfig
│       └── led_drivers/
│           ├── led_drv.c
│           ├── Makefile
│           └── Kconfig
├── arch/
│   └── arm/
│       └── boot/
│           └── dts/
└── include/
    └── linux/

二、两种添加驱动的方法

方法一:外部模块编译

  驱动源码放在内核源码目录外面,例如:

arduino 复制代码
内核源码树:/home/book/imx6ull-sdk/Linux-4.9.88
驱动源码:/home/book/driver/led/led_drv.c

我们需要在驱动源码目录下建立外部makefile,但是也需要借用内核源码的makefile进行编译,例如:

javascript 复制代码
KERN_DIR = /home/book/imx6ull-sdk/Linux-4.9.88

obj-m += led_drv.o                      //obj-m:Linux 内核 Kbuild 编译系统中的变量
                                        //m:编译为模块,不编译进内核
                                        //如果不写 obj-m += led_drv.o,Linux 内核编译
                                        //系统会进入你当前目录看 Makefile,但它不知道要
                                        //把哪个 .c 文件编译成 .ko,所以一般不会编译你的 led_drv.c
all:
	make -C $(KERN_DIR) M=$(PWD) modules  //make -C $(KERN_DIR):进入内核源码KERN_DIR目录,使用内核编译系统
                                        //M=$(PWD):告诉内核:外部模块源码在当前目录
                                        //modules:告诉内核:只编译模块,不是整个内核
clean:
	make -C $(KERN_DIR) M=$(PWD) clean

这种方式不会改内核源码树,也不需要 Kconfig。

方法二:加入内核源码树编译

  驱动源码放在内核源码目录下,例如:

arduino 复制代码
Linux-4.9.88/drivers/char/led_drivers/led_drv.c

我们需要修改以下目录,将该驱动类似于其他驱动一样加入到内核源码的目录

scss 复制代码
drivers/char/Makefile
drivers/char/led_drivers/Makefile
drivers/char/Kconfig
drivers/char/led_drivers/Kconfig

源码放进内核源码树
修改 Kconfig 和 Makefile
这里修改drivers/char/Makefile时,通常加上obj-y(或者$(CONFIG_LED_DRV)) += led_drivers/,
它的意思不是"把文件夹编译成 .o",而是:编译当前目录时,进入 led_drivers 子目录继续读取里面的 Makefile

通过 menuconfig 选择 y/m/n
执行 make 或 make modules 自动编译

三、Kconfig、menuconfig、.config、Makefile 的关系

整体关系:

ini 复制代码
Kconfig 文件
  |
  |  定义有哪些配置选项
  |  例如:CONFIG_LED_DRV
  v
make menuconfig
  |
  |  图形/菜单方式让用户选择 y / m / n
  v
.config 文件
  |
  |  保存最终选择结果
  |  例如:
  |  CONFIG_LED_DRV=y
  |  CONFIG_KEY_DRV=m
  v
Makefile
  |
  |  根据 CONFIG_xxx 的值决定编译进内核还是编译成模块
  v
生成内核镜像 zImage / Image 或 .ko 模块

1.Kconfig 是什么?

  Kconfig 是用来定义配置菜单和配置选项的文件。

  比如驱动目录中可能有一个 Kconfig:

arduino 复制代码
config LED_DRV                  //config LED_DRV最终会变为CONFIG_LED_DRV
    tristate "My LED Driver"
    default n
    help
      This is a simple LED driver.

它的意思是:

复制代码
定义了一个配置项:LED_DRV

在 menuconfig 中显示为:
My LED Driver

它可以选择:
y:编译进内核
m:编译成模块 .ko
n:不编译

2.menuconfig 是什么?

  menuconfig 不是一个文件,而是一个配置界面。

  当我们执行make menuconfig后会打开一个菜单界面,可以在里面选择:

css 复制代码
[*] 编译进内核                     //[*] -> y
[M] 编译成模块                     //[M] -> m
[ ] 不编译                         //[ ] -> n

例如:在 menuconfig 中选择了 LED 驱动,然后选择了编译成模块,保存退出后,.config 文件中会出现

ini 复制代码
CONFIG_LED_DRV=m

3..config 是什么?

  .config 是 menuconfig 保存出来的最终配置文件。它记录了用户选择了哪些功能,所以 .config 是内核编译时真正参考的配置结果。 例如:

ini 复制代码
CONFIG_LED_DRV=y
表示 LED 驱动编译进内核。

CONFIG_KEY_DRV=m
表示 KEY 驱动编译成 .ko 模块。

# CONFIG_BEEP_DRV is not set
表示 BEEP 驱动不编译。

4.Makefile 是什么?

  Makefile 决定具体编译哪些源码文件。

  比如某个驱动目录下的 Makefile:

javascript 复制代码
obj-$(CONFIG_LED_DRV) += led_drv.o
obj-$(CONFIG_KEY_DRV) += key_drv.o
obj-$(CONFIG_BEEP_DRV) += beep_drv.o

它会根据 .config 中的配置结果来决定编译方式,假设 .config 中是:

ini 复制代码
CONFIG_LED_DRV=y
CONFIG_KEY_DRV=m
# CONFIG_BEEP_DRV is not set

展开的结果如下:

复制代码
obj-y += led_drv.o
表示led_drv.o 编译进内核

obj-m += key_drv.o
表示key_drv.o 编译成 key_drv.ko 模块

obj-  += beep_drv.o
beep_drv.o 不编译

综上所述:Kconfig / menuconfig / .config 决定"某个功能要不要编、编进内核还是编成模块";Makefile 根据这个结果,决定"具体编译哪些 .c 文件,以及怎么组织生成目标文件"。

相关推荐
groundhappy12 小时前
idalib安装和codex ida-mcp配置
linux·开发语言·python
通信小小昕13 小时前
Ubuntu 26.04 中文输入法安装
linux·运维·ubuntu
张小姐的猫14 小时前
【Linux】网络编程 —— HTTP协议(上)
linux·运维·服务器·网络·http·单例模式·策略模式
栩栩云生14 小时前
AI 写代码犯的错,早被写进了错题集
linux·安全·ai编程
imc.1115 小时前
linux基础IO
linux·运维·服务器
BelongPanda17 小时前
Linux Nginx 纯手动 Let‘s Encrypt 泛域名证书配置教程
linux·nginx
酷可达拉斯17 小时前
Linux操作系统-shell编程(0)
linux·运维·服务器·python·云计算
2301_7779983418 小时前
Linux中断机制:操作系统如何高效运行
linux·运维·服务器
yunwei3718 小时前
eBPF 教程:检查 exec 后真正安装的可执行镜像
linux·开源
笑锝没心没肺18 小时前
yum update 报错:GPG key retrieval failed: [Errno 14] HTTPS Error 404 - Not Found
linux·运维·服务器