模块的编译与加载:从Makefile到内核空间
说实话,模块编译这块儿,我当年刚入门时也踩过不少坑。明明代码写得没问题,一加载就报错,那种感觉真的很抓狂。后来才明白,内核模块的编译和普通程序完全是两码事。今天我就把这块儿掰开了揉碎了讲给你听。
一、Makefile编写——模块编译的“施工图纸”
写Makefile,说白了就是告诉内核构建系统:我要编译什么、用什么编译、编译成啥样。我个人习惯把Makefile分成两部分:变量定义和编译规则。
核心要点:内核模块的Makefile不是直接调用gcc,而是通过内核的Kbuild系统来编译。
来看一个最基础的Makefile模板:
# 最简单的单文件模块
obj-m := hello.o
# 如果有多个源文件,这样写
# hello-objs := main.o helper.o
# obj-m := hello.o
# 指定内核源码路径
KDIR := /lib/modules/$(shell uname -r)/build
# 当前目录
PWD := $(shell pwd)
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean
这里有个关键点:obj-m 表示编译成模块,obj-y 才是编译进内核。我曾经见过有人把 obj-m 写成 obj-y,结果模块死活编译不出来——嗯,这种低级错误我也犯过。
小技巧:如果你在交叉编译,记得设置 ARCH 和 CROSS_COMPILE 变量。比如:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
二、模块编译过程——到底发生了什么?
你执行 make 的时候,背后其实发生了这么几件事:
- Kbuild系统读取你的Makefile,找到
obj-m指定的模块 - 进入内核源码目录,加载内核的配置和头文件
- 编译你的
.c文件,生成.o目标文件 - 链接成
.ko内核模块文件
为什么会这么复杂?因为内核模块必须和当前内核版本、配置完全匹配。你想想看,内核在运行时管理着整个系统的资源,一个不匹配的模块加载进去,轻则崩溃,重则数据丢失。
下面这张图展示了整个编译流程:
三、insmod/lsmod/rmmod——模块管理的三剑客
模块编译好了,怎么用?这三个命令你得烂熟于心。
3.1 insmod——加载模块
sudo insmod hello.ko
就这么简单?嗯,但前提是你的模块没有依赖问题。如果模块依赖其他模块,你得先加载依赖。我曾经在调试一个USB驱动时,忘了先加载usbcore模块,结果insmod直接报错:Unknown symbol。
注意:insmod需要root权限。而且它不会自动处理依赖关系,这点和modprobe不同。
3.2 lsmod——查看已加载模块
lsmod
这个命令会列出所有已加载的模块,包括:
| 列名 | 含义 | 示例 |
|---|---|---|
| Module | 模块名称 | hello |
| Size | 模块占用内存大小 | 16384 |
| Used by | 被哪些模块或进程使用 | 0 |
你看,Used by 这一列特别重要。如果显示不为0,说明有其他模块或进程正在使用它,这时候你rmmod会失败。
3.3 rmmod——卸载模块
sudo rmmod hello
卸载时要注意:
- 模块不能被其他模块依赖
- 模块不能被用户空间程序使用
- 模块的退出函数必须正确执行
调试技巧:如果rmmod失败,用 lsmod | grep 模块名 看看引用计数。我曾经遇到过模块引用计数不为0,查了半天发现是另一个驱动在调用它的接口。
四、模块依赖与自动加载——让系统帮你干活
实际项目中,很少有模块是孤立的。比如一个USB摄像头驱动,可能依赖 videodev、usbcore、v4l2 等多个模块。
模块依赖信息存在哪里?在 /lib/modules/$(uname -r)/modules.dep 文件里。这个文件由 depmod 命令生成。
# 手动更新依赖
sudo depmod -a
# 查看某个模块的依赖
modinfo -F depends hello.ko
自动加载怎么实现?有两种方式:
- /etc/modules:把模块名写进去,系统启动时自动加载
- udev规则:当检测到特定硬件时,自动加载对应驱动
实战经验:我在做嵌入式项目时,经常把模块放在 /lib/modules/$(uname -r)/extra/ 目录下,然后运行 depmod -a。这样 modprobe 就能自动处理依赖了。说白了,modprobe 比 insmod 智能得多,它会自动加载所有依赖。
最后送你一句话:模块编译和加载,看似简单,但每个细节都可能让你翻车。多动手、多踩坑,慢慢就熟练了。