内核模块 动态编译
不同目录编译(动态编译):out-of-tree
基于内核的框架,需事先编译好内核(依赖文件 Module.symvers、vmlinux)。模块开发大多使用此方法。
新目录包含驱动源码,用特定格式 Makefile 编译。
将生成的 .ko 文件拷贝到要安装的Linux环境。
使用 insmod 安装驱动插件。
模块 Makefile 根据指定内核配置文件编译。
Makefile 说明
/Documentation/kbuild/modules.txt
/Documentation/kbuild/makefiles.txt
Makefile 变量:
__KERNEL__ :表示编译入内核。
KERNELRELEASE:内核版本号。
MODULE:表示编译成模块。
EXTRA_FLAGS:额外的外部变量,不会覆盖内部。
KBUILD_EXTRA_SYMBOLS:模块的符号表文件。通常为编译模块依赖的模块目录下的 Module.symvers
如果多个相互依赖的模块在一个 Makefile 中编译,则可省略。被依赖而模块放在前。
xxx/linux/version.h:版本定义头文件。
/lib/modules/$(shell uname -r)/build:本系统内核构建源码的构建文件。
解决报错:apt install linux-headers-$(uname -r)
与 arm 区分:ifeq ($(ARCH),arm)
Makefile 解析
模块 Makefile 核心思想:-C 进入内核路径,获取内核信息(编译器、架构等);M 指定被编译模块的目录
即依赖进入的内核目录,执行当前目录下的 Makefile 编译 modules。
$(MAKE) -C <KERNELDIR>/ M=`pwd` modules
$(MAKE) -C <KERNELDIR>/ M=`pwd` modules clean
// $(MAKE) -C <KERNELDIR>/ M=`pwd` modules_install
// $(MAKE) -C $(KERNELDIR) M=$(PWD) modules INSTALL_MOD_PATH=$(path) modules_install
---------------------------------------
解决重复读取变量:
make -C 读入内核 Makefile 并传入本 Makefile 地址做参数,会引起两次读取
使用如下语句做区分:(Makefile 详见附件)
ifneq ($(KERNELRELEASE),)
else
endif
---------------------------------------
驱动模块多文件编译 + 多模块编译:TARGET 不能与任何依赖重名。
TARGET = <module_name1>
obj-m += $(TARGET).o
$(TARGET)-objs := a.o b.o ...
obj-m += <module_name2>.o
<module_name2>-objs += b.o c.o ...
---------------------------------------
引用其他模块符号:如果多个相互依赖的模块在一个 Makefile 中编译,则可省略。被依赖而模块放在前。
KBUILD_EXTRA_SYMBOLS += <module1_path>/Module.symvers
+= <module2_path>/Module.symvers
... ...
export KBUILD_EXTRA_SYMBOLS
Kbuild 与 Makefile
新版本内核在编译时会先从 Kbuild 中寻找目标和依赖,然后再去 Makefile 中寻找。
即可以将 ifneq ($(KERNELRELEASE),) 中的内容放入和 Makefile 同级目录的 Kbuild 中。
为保证兼容,ifneq ($(KERNELRELEASE),) 下可以直接只包含 Kbuild(include Kbuild)。
Linux内核模块编译、加载、运行机制分析、版本控制、许可声明、内核污染、模块传参、模块签名机制、out-of-tree动态模块编译及Makefile模板编写,尽在《Linux内核编程》,详情点击:王利涛老师个人淘宝店:Linux内核编程