当前位置: 首页 > news >正文

Linux驱动学习之模块化,参数传递,符号导出

1.模块化

1.1.模块化的基本概念:

  • 模块化是指将特定的功能或组件独立出来,以便于开发、测试和维护。在Linux设备驱动中,模块化允许将驱动程序作为内核模块动态加载到系统中,从而提高了系统的灵活性和可扩展性。

1.2.Linux内核模块的特点

  • 动态加载和卸载:内核模块可以在系统运行时被动态加载或卸载,而无需重新编译整个内核。
  • 更好的扩展性:系统可以根据需要加载或卸载模块,从而避免资源的浪费。
  • 易于管理和维护:模块化的设计使得驱动程序的开发、测试和部署更加便捷

 1.3.模块化的好处

  • 独立:模块之间可以独立开发。
  • 隔离:驱动模块与内核隔离,不需要改内核源码,不然来一个需求改一次内核源码,风险增加,而且编译内核,换内核都是相对麻烦(当然使用ftp导入内核没什么)。

 1.4.驱动模块主要由以下几个部分组成:

模块加载函数(必须:当通过insmod命令加载内核模块时,模块的加载函数会自动被内核执行,完成本模块相关初始化工作;
模块卸载函数(必须):当通过rmmod命令卸载模块时,模块的卸载函数会自动被内核执行,完成与模块加载函数相反的功能;
模块许可证声明(必须):模块许可证(LICENCE)声明描述内核模块的许可权限,如果不声明LICENCE,模块被加载时将收到内核被污染的警告。大多数
模块参数(可选):模块参数是模块被加载的时候可以被传递给他的值,它本身对应模块内部的全局变量;
模块导出符号(可选):内核模块可以导出符号(symbol,对应于函数或变量),这样其他模块可以使用本模块中的变量或函数;
模块作者等信息声明(可选)。

2.驱动模块的相应接口

module_init(xxx_init);                           //注册模块加载函数
module_exit(xxx_exit);                         //注册模块卸载函数
MODULE_LICENSE( "GPL" )                 //添加模块 LICENSE 信息
MODULE_AUTHOR( "你的名字" )         //添加模块作者信息
module_param(name,type,perm)         //给模块传递参数
参数:
@name用来接收参数的变量名
@type参数的数据类型
@perm指定参数访问权限。
module_param_named(name_out,name_in,type,perm)   //给模块传递参数
参数:
@name_out在加载模块时,参数的名字
@name_in模块内部变量的名字
@type 参数类型
@perm 访问权限
MODULE_PARM_DESC(name,describe);          //给模块里面参数的变量指定一个描述信息,通过modinfo可以查看到
                参数
                    @name 变量名
                    @describe描述信息的字符串
EXPORT_SYMBOL(name);        //导出符号
EXPORT_SYMBOL_GPL(name);
参数类型:
MODULE_LICENSE("GPL");          
// "GPL" 是指明了 这是GNU General Public License的任意版本
                // “GPL v2” 是指明 这仅声明为GPL的第二版本
               // "GPL and addtional"
               // "Dual BSD/GPL"
               // "Dual MPL/GPL"
              // "Proprietary"  私有的
注意:除非你的模块显式地声明一个开源版本,否则内核会默认你这是一个私有的模块(Proprietary)。

 其他接口:

MODULE_DESCRIPTION("描述")         // 对这个模块作一个简单的描述,modinfo可以查看
MODULE_VERSION                  // 这个模块的版本
MODULE_ALIAS                    // 这个模块的别名
MODULE_DEVICE_TABLE             // 告诉用户空间这个模块支持什么样的设备,当系统检测到匹配这些ID设备时,就可以自动加载这个模块

3.测试以及测试解释

3.1.模块化接口测试

该测试用例没有加模块协议,运行会报“模块许可证“未指定”污染内核”,所以该加还是加上。

3.2.参数传递

#include <linux/init.h>
#include <linux/module.h>
static unsigned int var=0;
module_param(var,uint,0664);static char *string;
module_param(string,charp,0444);
MODULE_PARM_DESC(var, "int value");
MODULE_PARM_DESC(string, "str value");static int hello_init(void)
{printk("hello my drivce \r\n");printk("int value %u \r\n", var);printk("str value %s \r\n", string);return 0;
}
static void hello_exit(void)
{printk("hello_exit \r\n");printk("int value %u \r\n", var);printk("str value %s \r\n", string);return;
}MODULE_AUTHOR("seven");
MODULE_LICENSE("GPL");
module_init(hello_init);
module_exit(hello_exit);
MODULE_DESCRIPTION("module test.");

测试:

加载驱动可以传递参数如:(insmod 01_module_test.ko string="seven" var=10),这里初始化的时候不设置参数,通过修改 /sys/module/01_module_test/parameters/变量名 这个文件去修改对应的值,然后卸载程序的时候打印出来看看。

 3.3.符号导出

a驱动代码(a驱动导出符号给b驱动使用)

#include <linux/init.h>
#include <linux/module.h>
static int global_var = 100;
static void show(void)
{printk("a module show():  global_var =%d \n",global_var);
}
static int hello_init(void)
{printk("a module export :global_var=%d\n",global_var);return 0;
}
static void hello_exit(void)
{printk("hello_exit \n");return;
}EXPORT_SYMBOL(global_var);
EXPORT_SYMBOL(show);
MODULE_AUTHOR("seven");
MODULE_LICENSE("GPL");
module_init(hello_init);
module_exit(hello_exit);

b驱动代码:

#include <linux/module.h>extern int global_var;
extern  void show(void);
static int hello_init(void)
{printk("module b: global_var= %d\n",global_var);global_var--;show();printk("module b: global_var= %d\n",global_var);global_var--;show();return 0;
}
static void hello_exit(void)
{printk("hello_exit \n");return;
}
MODULE_AUTHOR("seven");
MODULE_LICENSE("GPL");
module_init(hello_init);
module_exit(hello_exit);

 运行时候

3.3.1.b驱动的注意事项

b驱动编译需要依赖 a驱动的符号表(Module.symvers),解决这种问题,目前知道两种办法:

方法1:将a驱动下面的Module.symvers拷贝到b驱动构建目录下面,再进行编译

方法2:makefile里面加上,KBUILD_EXTMOD=构建a驱动的绝对路径

模块编译时,寻找使用的符号

  • a.在本模块中符号表中,寻找符号(函数或变量实现)
  • b.在内核全局符号表中寻找
  • c.在模块目录下的Module.symvers文件中寻找 

参考:

手把手教Linux驱动1-模块化编程_要求:掌握linux模块编程技术,了解linux驱动编程与编译。 内容: 第一步(必做):在li-CSDN博客

手把手教Linux驱动2-之模块参数和符号导出_linux 引用其他模块导出来的符号-CSDN博客

 内核Module.symvers文件揭秘 - Linux内核编程 | 宅学部落 (zhaixue.cc)

Linux设备驱动的模块化之路-CSDN博客

linux 内核模块声明 MODULE_LICENSE_module liciense-CSDN博客

http://www.lryc.cn/news/353032.html

相关文章:

  • RabbitMQ02-RebbitMQ简介及交换器
  • Matlab自学笔记三十:元胞数组的修改、添加、删除和连接
  • 【LeetCode】数组——双指针法
  • react 低代码平台方案汇总
  • oss对象上传文件设置格式
  • 【Linux学习】进程
  • Python数据分析实验四:数据分析综合应用开发
  • 基于51单片机的盆栽自动浇花系统
  • SpirngMVC框架学习笔记(一):SpringMVC基本介绍
  • 实现信号发生控制
  • 二叉树基于队列实现的操作详解
  • LabVIEW常用开发架构有哪些
  • 告别 Dart 中的 Future.wait([])
  • Cisco ASA防火墙抓包命令Capture
  • Linux网络编程:HTTP协议
  • HTTP 协议中 GET 和 POST 有什么区别?分别适用于什么场景?
  • talib 安装
  • echarts-树图、关系图、桑基图、日历图
  • 04Django项目基本运行逻辑及模板资源套用
  • 安徽大学数学科学学院教授陈昌昊
  • com.alibaba.fastjson.JSONObject循环给同一对象赋值会出现“$ref“:“$[0]“现象问题
  • 【C++】详解AVL树——平衡二叉搜索树
  • 《计算机网络微课堂》2-2 物理层下面的传输媒体
  • 【算法设计与分析】基于Go语言实现动态规划法解决TSP问题
  • Golang单元测试
  • mac下安装airflow
  • 二进制中1的个数c++
  • 【面试干货】数据库乐观锁,悲观锁的区别,怎么实现
  • 移动端仪表盘,支持更多组件
  • 科技产业园3D探秘:未来科技之城的奇幻之旅