模块是一种向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
函数实现移除模块。具体源码如下: