目录
一、模块简介
作用:
模块是为了方便驱动程序的加载或卸载。
原理:
由于Linux可运行于各种各样的体系架构(内核源码--arch文件--ARM--RISC-V(开源))中,并且支持无数的I/O设备,把所有要支持的设备都直接编译进内核并不合适,所以Linux发行版通常包含一个最小的内核镜像(包含通用的基础的功能),而以模块的形式提供其他的功能(场景:设备--在进行成品保证的时候,要判断,这个成品是否完整),在系统运行时,可以动态地按需加载模块。静态--是指内核里面已经包含的驱动模块
补充:当系统启动之后,可以采用动态加载的方式进行模块的加载或卸载
使用Linux模块的优点:
(1)方便扩展Linux系统的功能。
(2)方便更新Linux设备驱动。 /采用的直接替换--调试 --
(3)增加新功能或更新驱动时,无需重新编译整个内核--kernel(比较耗时),只需编译 安装相应的驱动模块即可。
(4)减少Linux内核的体积(将不需要的功能裁剪掉),节省存储空间。
(5)内核开发与模块开发独立开来,效率更高。
常见的模块:
(1)Linux系统非必须的驱动程序(必要的驱动程序已经整合到内核代码中了)
(2)供其他程序使用的工具函数集合,类似于用户空间的库函数--模块加载到内核。
(3)Linux系统非必须的,扩展的功能--需要手动的形式进行加载或卸载。
二、模块的基本构成
最基本的是一下两个头文件,后续用到的宏都在这两个头文件中定义
#include <linux/module.h>
#include <linux/init.h>
#include <内核提供的其他头文件.h>
#include <自定义的头文件.h>
注意:
在模块代码中不能使用任何标准C库提供的函数,即不能包含标准C库头文件,如#include <stdio.h> #include<string.h>、#include <unistd.h>等,也不能进行浮点运算。
内核为模块提供了专用的字符串处理接口<对应头文件#include <linux/string.h>>和打印函数printk(定义在<#include <linux/printk.c>>)。
全局变量的定义和子函数的实现:
第一种:全局变量和C语言中全局变量类似。
第二种:模块与模块之间也可以传递参数。
作用:
如果定义的全局变量或函数需要导出给其他模块使用,需要在全局变量定义或函数实现之后做模块导出的宏声明。称为符号导出。
宏的写法两种方式:
(1)第一种:不指定许可证(开源声明)的情形,即不做许可证声明的模块也可以使用该全局变量或函数
EXPORT_SYMBOL(需要导出的变量名或函数名)
例子:
EXPORT_SYMBOL(irongate_ioremap);
(2)第二种:指定许可证情形,必须做GPL、GPLv2或Dual BSD/GPL 许可证声明的模块才可以使用该全局变量或函数。
EXPORT_SYMBOL_GPL(需要导出的变量名或函数名)
实例参考:
EXPORT_SYMBOL_GPL(ep93xx_chip_revision);
如果全局变量需要在模块加载时传入参数,则需要在全局变量定义之后用模块参数的宏声明,称为模块传参:
单模块传递参数的时候,可以传递单变量 ,也可以数组。
宏参数的说明:
(1)name :需要传入参数的变量名。
(2)type:参数类型,需要与变量类型兼容
变量的写法:
(1)第一种:非数组的单个变量的情形
module_param(name,type,perm);
name:对应变量的名字
type:对应变量的类型
perm:访问的权限 0664
(2)第二种:数组变量的情形
Int num ; // 必须要提前定义一个int型的变量,用于存储传给数组变量的参数个数,名称自定义。
module_param_array(name, type,&num,perm);
注意:如果传入参数的个数n少于数组元素个数,则参数赋值给前n个元素,后面的元素保留原来定义的值。