01、Linux内核模块
一、Linux内核模块简介
1、 模块的特点
- 模块本身不被编译到内核镜像中,从而控制了内核的大小
- 模块一旦被加载,就能和内核中的其他部分一样被调用
2、模块操作的命令
命令 | 说明 |
---|---|
insmod xxx.ko | 加载模块 |
modprobe xxx.ko | 加载模块,并同时加载该模块所依赖的其他模块 |
rmmod xxx.ko | 卸载模块 |
moprobe -r xxx.ko | 卸载模块,并同时卸载该模块所依赖的其他模块 |
lsmod | 查看已经加载的模块及模块间的依赖关系 |
modinfo xxx.ko | 获取模块的信息 |
模块加载完成后将在
/sys/module
目录下生成对应名称的目录
二、内核模块程序结构
结构 | 说明 |
---|---|
模块加载函数 | 通过模块加载命令加载时,函数自动被内核执行,完成模块相关的初始化工作 |
模块卸载函数 | 通过模块卸载命令卸载时,函数自动被内核执行,完成和加载相反的工作 |
模块许可证说明 | 声明模块的许可权限,不声明将会产生内核被污染的警告 |
模块参数(可选) | 模块加载时可以传递给模块的值,该值也是模块内部的全局变量 |
模块导出符号(可选) | 模块可以导出的符号(包含函数或变量),符号导出后可被其他模块使用 |
模块作者信息(可选) |
三、模块加载函数
模块加载函数一般以__init
标识声明,该标识告诉内核在初始化调用后可以将函数占用的空间释放留作他用。
#include <linux/init.h>
#include <linux/module.h>
/**
* @brief 模块加载函数
*
* @return int --0表示初始化成功,负值表示初始化失败
*/
static int __init xxx_init(void)
{
/* 初始化代码 */
return 0;
}
module_init(xxx_init);
四、模块卸载函数
模块加载函数一般以__exit
标识声明,该标志告诉内核如果模块直接编译进内核,则模块卸载函数不链接到最后的镜像中。
#include <linux/init.h>
#include <linux/module.h>
/**
* @brief 模块卸载函数
*/
static void __exit xxx_exit(void)
{
/* 释放资源代码 */
}
module_exit(xxx_exit);
五、模块参数
使用module_param
宏可以为模块定义一个参数,使用module_param_array
宏可以为模块定义一个参数数组。
/**
* @brief 定义一个模块参数
*
* @param name --参数名
* @param type --参数类型
* @param perm --参数读写权限
*/
module_param(name, type, perm);
/**
* @brief 定义一个模块参数数组
*
* @param name --参数数组名
* @param type --参数数组类型
* @param nump --参数数组长度
* @param perm --参数读写权限
*/
module_param_array(name, type, nump, perm);
在加载模块时调用命令后边加上参数及对应的值即可对参数进行赋值,即insmod xxx.ko param=value
或modprobe xxx.ko param=value
。如果不传递,参数将使用模块内定义的缺省值。
参数类型可以为byte
(单字节类型)、short
、ushort
(有无符号短整型)、int
、uint
(有无符号整型)、long
、ulong
(有无符号长整型)、charp
(字符串指针)、bool
、invbool
(正反布尔类型)。
模块加载完成后,会在/sys/module/xxx/parameters/
(xxx表示模块名称)目录下生成声明的参数对应的文件,该文件拥有声明时的操作权限。
六、导出符号
使用EXPORT_SYMBOL(sys)
宏声明导出符号到内核符号表中/proc/kallsyms
。
七、模块声明与描述
宏 | 说明 |
---|---|
MODULE_AUTHOR | 模块作者 |
MODULE_DESCRIPTION | 模块描述信息 |
MODULE_VERSION | 模块版本信息 |
MODULE_DEVICE_TABLE | 模块设备表,对于USB、PCI设备驱动通常会创建设备表 |
MODULE_ALIAS | 模块别名 |
八、模块的使用计数
九、模块的编译
模块的编译需要在linux内核路径下编译,因此需要定义KERNAL_PATH
和CUR_PATH
变量来告诉make执行的路径。该Makefile文件应该驱动源代码在同一目录下。
KERNAL_PATH := #内核存储的绝对路径
CURRENT_PATH := $(shell pwd)
MODULE_NAME := #编译模块的名称
obj-m := $(MODULE_NAME).o
#如果模块包含多个.c文件组成,则需要使用下一行命令
modulename-objs := file1.o file2.o
all:
clear #清空终端内容
$(MAKE) -C $(KERNAL_PATH) M=$(CURRENT_PATH) modules
clean:
$(MAKE) -C $(KERNAL_PATH) M=$(CURRENT_PATH) clean