Linux内核模块开发完全指南1. 内核模块基础概念1.1 什么是内核模块内核模块是Linux内核的可动态加载组件允许在不重新编译整个内核的情况下扩展系统功能。模块以.koKernel Object文件形式存在可以在运行时加载和卸载。1.2 模块与静态编译的区别与直接编译进内核的代码相比模块具有以下特点动态加载/卸载减少内核内存占用开发调试周期短无需重启系统功能隔离单个模块崩溃不会导致整个系统崩溃2. 最简单的内核模块实现2.1 基本代码结构一个最简单的内核模块包含以下必要元素#include linux/init.h #include linux/kernel.h #include linux/module.h static int __init my_init(void) { printk(my_init\n); return 0; } static void __exit my_exit(void) { printk(my_exit\n); } module_init(my_init); module_exit(my_exit);2.2 关键组件解析头文件linux/module.h包含模块编程的基本宏和函数linux/kernel.h提供printk等内核函数linux/init.h定义模块初始化和退出相关宏初始化函数使用__init宏标记表示该函数仅在初始化时使用必须返回int类型0表示成功退出函数使用__exit宏标记表示该函数仅在模块卸载时使用模块声明module_init()指定模块加载时调用的函数module_exit()指定模块卸载时调用的函数3. 模块编译与加载3.1 Makefile编写典型的内核模块Makefile示例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) clean3.2 多文件模块编译当模块由多个源文件组成时Makefile需要特殊处理obj-m : hello_world.o hello_world-objs : hello.o world.o4. 模块操作命令4.1 模块加载insmod# insmod drv.ko直接加载指定路径的.ko文件不处理依赖关系modprobe# depmod # modprobe drv自动处理模块依赖从标准模块目录查找模块4.2 模块卸载# rmmod drv卸载已加载的模块如果模块正在使用会返回EBUSY错误4.3 模块信息查看# modinfo drv输出示例filename: /lib/modules/3.13.0-32-generic/drv.ko srcversion: 533BB7E5866E52F63B9ACCB depends: vermagic: 3.13.0-32-generic SMP mod_unload modversions 6865. 内核打印与调试5.1 printk函数内核空间使用printk代替用户空间的printfprintk(KERN_INFO Debug message: %d\n, value);注意事项不支持浮点数打印消息级别通过KERN_*宏指定输出到内核环形缓冲区可通过dmesg查看5.2 常见打印问题尝试打印浮点数会导致链接错误WARNING: __extendsfdf2 undefined! WARNING: __truncdfsf2 undefined!6. 模块参数传递6.1 参数类型支持内核模块支持以下参数类型bool/invbool布尔值charp字符串指针整型short/int/long及其无符号版本数组上述类型的数组6.2 参数定义示例static int baudrate 9600; static int port[4] {0, 1, 2, 3}; static char *name user; module_param(baudrate, int, S_IRUGO); module_param_array(port, int, NULL, S_IRUGO); module_param(name, charp, S_IRUGO);6.3 参数传递方式加载时指定参数# insmod user.ko baudrate115200 port1,2,3,4 namevirtual-serial7. 内核模块开发注意事项7.1 内核污染Tainting加载模块时可能出现警告loading out-of-tree module taints kernel可能原因模块未声明GPL协议内核版本不匹配使用树外模块7.2 C库限制内核模块不能使用标准C库函数因为内核运行在比C库更低级别内核实现了自己的基础函数版本内存管理方式与用户空间不同替代方案kmalloc代替mallocprintk代替printf内核提供的字符串操作函数8. 高级模块开发技巧8.1 初始化调用顺序内核提供多种初始化级别core_initcall核心子系统初始化postcore_initcall核心初始化之后arch_initcall架构特定初始化subsys_initcall子系统初始化fs_initcall文件系统初始化device_initcall设备驱动初始化late_initcall最后阶段的初始化8.2 符号导出允许其他模块使用本模块的函数/变量EXPORT_SYMBOL(my_function); EXPORT_SYMBOL_GPL(my_function); // 仅GPL模块可用8.3 版本控制确保模块与内核版本兼容MODULE_LICENSE(GPL); MODULE_AUTHOR(Your Name); MODULE_DESCRIPTION(Module description); MODULE_VERSION(1.0);