模块是一种向Linux内核添加设备驱动程序、文件系统及其他组件的有效方法,不需要编译新内核
modprobe和insmod。modprobe在识别出目标模块所依赖的模块后,在内核也会使用insmod,再用户空间队模块的处理也是基于insmod。printk或者kmalloc。proc文件系统访问,即文件/proc/kallsyms。
用户空间工具和内核的模块实现之间的接口,包括两个系统调用。
init_module:将一个新模块插入到内核中。用户空间工具只需提供二进制数据。所有其他工作(特别是重定位和解决引用)由内核自身完成。delete_module:从内核移除一个模块。当然,前提是该模块的代码不再使用,并且其他模块不再使用该模块导出的函数。
还有一个
request_module函数(不是系统调用),用于从内核端加载模块。它不仅用于加载模块,还用于实现热插拔功能。
在详细讲解模块相关函数实现之前,有必要解释如何在内核中表示模块(及其属性)。首先需要定义一组数据结构。首先需要定义一组数据结构。其中,module是最重要的数据结构。内核中驻留的每个模块,都分配了该结构的一个实例。其定义如下:

state表示模块当前的状态,可以从枚举类型moudule_state取值。

syms、num_syms和crc用于管理模块导出的符号。syms是一个数组,有num_syms个数组项,数组项类型为kernel_symbol,负责将标识符(name)分配到内存地址(value):

如果模块B依赖模块A提供的函数,那么模块A和模块B之间就存在关系。可以用两种不同的方式来看这种关系。

中的module_init和module_exit宏用于定义init函数和exit函数。
内核为导出符号提供了两个宏:EXPORT_SYMBOL和EXPORT_SYMBOL_GPL。顾名思义,二者分别用于一般的导出符号和只用于GPL兼容代码和导出符号。同样,其目的在于将相应的符号放置到模块二进制映像的适当段中。
模块许可证、开发者和描述、备选名称、基本版本控制
init_module系统调用是用户空间和内核之间用于装载新模块的接口
init_module系统调用是用户空间和内核之间用于装载新模块的接口,通过load_module函数将二进制数据传输到内核地址空间中。具体源码如下:


从内核删除模块比插入模块简单的多,系统调用delete_module函数实现移除模块。具体源码如下:
