本系列参考陈学松的《深入Linux设备驱动程序内核机制》
Linux内核模块形式上以.ko文件存在,概念上类似于Windows的动态链接库dll,内核模块可以在系统运行期间动态扩展系统功能而无须重新编译一个新的内核镜像并重启系统,这一特性为内核开发者提供了极大的便利。
1. 内核模块的动态编译和静态编译
首先得了解两者的区别,静态编译模块直接进内核镜像,动态编译模块生成.ko文件。本文主要讨论模块的动态加载。
参考一个简单的实例 http://www.linuxidc.com/Linux/2011-09/42068.htm
2 内核模块.ko的文件格式ELF
用命令file查看,ko文件,显示 ELF 32-bit LSB relocatable ... ...
Linux下常见的可执行文件都是以ELF的形式存在。
section是ELF的主体,位于文件视图中间部分的一个连续区域,模块被加载时,被分配到一块内存区域。
3 内存视图 (HDR视图)
用户空间程序insmod首先通过文件系统接口读取内存模块的文件数据,放在一块用户空间的存储区域,然后通过系统调用sys_init_module和里面的load_module,后者将vmalloc分配一块内存空间,copy_from_user复制数据,在内核空间构造出ELF的内存视图。
之后,内存视图留下CORE section部分搬移到一个最终的内存地址,这部分数据是模块在系统中整个存活期会使用到的数据。
4 INIT和CORE
根据ELF spec,标记有SHF_ALLOC的section表示在模块的运行过程中,需要占用内存空间,其分为两大类,INIT和CORE,INIT section所在的内存区域会被最终释放,CORE section中的数据将一直驻留在内存中。
5 模块的“未解决的引用” (unsolved symbol)
内核模块ELF中的“未解决的引用”符号,是指当模块的编译工具链对模块进行链接生成.ko文件时,对于模块中调用的一些函数,链接工具无法在该模块的所有目标文件中找到这个函数的具体指令码,只能暂时对这个函数标记为“未解决的引用”,对它的处理将一直延续到内核模块被加载时从内核或者其他模块找到这个函数。
6 模块导出符号 (EXPORT_SYMBOL)
模块不仅使用内核和其他模块导出的符号,而且向外部导出自己的符号供内核使用。
7. 模块的参数
可以向内核模块传递一些参数,比如 # insmod devdemo.ko dolphin=10 bobcat=5
驱动程序里应声明模块参数如下,
/* devdemo.c */ #include <linux/module.h> #include <linux/kernel.h> int dolphin; int bobcat; module_param(dolphin, int, 0); module_param(bobcat, int, 0);
8. 模块的版本控制
内核和模块之间协商出一种机制确保不同版本的模块中的接口可以使用,Linux对此的解决方案是使用接口的校验和,也叫CRC校验码。基本思想就是根据函数的参数生成一个大小为4个字节的CRC校验码,当双方的校验码相等时视为相同接口,否则为不同接口。内核通过CONFIG_MODVERSIONS启用版本控制特性。模块源码所在目录下的.mod.c文件记录了内核导出的函数接口和其对应的CRC校验码。