1、Makefile源码
#ubuntu的内核源码树,如果要编译在ubuntu中安装的模块
#KERN_VER = $(shell uname -r)
#KERN_DIR = /lib/modules/$(KERN_VER)/build
# linux内核的源码树目录
KERN_DIR = ~/kernel
obj-m += module.o #构造的模块名字是module.ko
module-objs :=file1.o file2.o #模块依赖file1.o和file2.o两个源文件
all:
make -C $(KERN_DIR) M=`pwd` modules
.PHONY: clean
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
(1)KERN_DIR:表示编译驱动所依赖的内核源码树的路径。内核源码要和加载驱动的内核一致,否则可能报错; (2)make -C:这是Makefile的命令,表示跳转到-C后指定的目录执行该目录下的Makefile。其实就是去执行内核的顶层Makefile,通过传参给顶层 Makefile编译成驱动; (3)M=pwd:Makefile在构造modules目标之前返回到模块源代码目录; (4)modules:标明是要编译成驱动文件,具体要去看内核顶层Makefile中对modules传参的处理;
2、xxx.mod.c文件
2.1、xxx.mod.c文件源码
#include <linux/module.h>
#include <linux/vermagic.h>
#include <linux/compiler.h>
MODULE_INFO(vermagic, VERMAGIC_STRING);
struct module __this_module
__attribute__((section(".gnu.linkonce.this_module"))) = {
.name = KBUILD_MODNAME,
.init = init_module,
#ifdef CONFIG_MODULE_UNLOAD
.exit = cleanup_module,
#endif
.arch = MODULE_ARCH_INIT,
};
static const char __module_depends[]
__used
__attribute__((section(".modinfo"))) =
"depends=";
2.2、xxx.mod.c文件分析
(1)xxx.mod.c是编译驱动的时候自动产生的,假设我们编译module.ko,当编译时就会产生一个module.mod.c文件,文件内容如上; (2)文件里定义了struct module类型的变量__this_module,__this_module变量初始化的信息就是用来描述驱动,这个结构体会被链接进ko文件中, 将来在加载ko驱动时,内核中会有代码将这个结构体从ko文件中解析出来,并根据这个结构体来加载驱动; (3)init_module:这个函数是驱动代码中用module_init宏声明的驱动加载函数,init_module是驱动加载函数的统一别名; (4)cleanup_module:这个函数是驱动代码中用module_exit宏声明的驱动卸载函数,cleanup_module是驱动卸载函数的统一别名; 补充:module_init宏和module_exit宏参考博客:;
2.3、Module.symvers文件
//驱动代码中
EXPORT_SYMBOL(test_chrdev_write);
//终端命令行
root@ubuntu: ls
Makefile modules.order Module.symvers module_test.c module_test.ko module_test.mod.c module_test.mod.o module_test.o
root@ubuntu:
root@ubuntu:/root/windows_share/driver/char_dev/code/3.CharDevSenior/5.3.11# cat Module.symvers
0x00000000 test_chrdev_write /root/windows_share/driver/char_dev/code/3.CharDevSenior/5.3.11/module_test EXPORT_SYMBOL
(1)在编译过的驱动代码目录中会产生Module.symvers文件,该文件记录了驱动中导出到内核的符号; (2)上面的Module.symvers文件中导出了test_chrdev_write符号,是因为在驱动代码中用EXPORT_SYMBOL宏导出了test_chrdev_write符号; (3)如果其他的驱动模块在编译时依赖该驱动的test_chrdev_write符号,则将Module.symvers文件拷贝到用到的驱动代码目录下,再进行编译,这样就不会报找不到符号的错误;