当前位置: 首页 > 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博客

相关文章:

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

1.模块化 1.1.模块化的基本概念&#xff1a; 模块化是指将特定的功能或组件独立出来&#xff0c;以便于开发、测试和维护。在Linux设备驱动中&#xff0c;模块化允许将驱动程序作为内核模块动态加载到系统中&#xff0c;从而提高了系统的灵活性和可扩展性。 1.2.Linux内核模…...

RabbitMQ02-RebbitMQ简介及交换器

一. AMQP协议 什么是AMQP协议 AMQP(Advanced Message Queuing Protocol,高级消息队列协议):它是进程之间传递异步消息的网络协议 AMQP工作过程 发布者通过发布消息&#xff0c;通过交换机&#xff0c;交换机根据路由规则将收到的消息分发交换机绑定的下消息队列&#xff0c;最…...

Matlab自学笔记三十:元胞数组的修改、添加、删除和连接

1.说明 元胞数组的子数组或元素也是元胞型的&#xff0c;其元素内容&#xff08;值&#xff09;是本身类型&#xff0c;因此&#xff0c;在添、删、改和连接处理时&#xff0c;必须明确每个元素的值的类型和大小&#xff0c;否则&#xff0c;编程报错是不可避免的了。看本文前…...

【LeetCode】数组——双指针法

1 双指针法 1.1 介绍 双指针法是一种常用的算法技巧&#xff0c;通常用于处理数组或链表中的问题。它使用两个指针&#xff0c;通常一个从数组的开始位置遍历&#xff0c;另一个从数组的末尾位置开始遍历&#xff0c;根据问题的不同&#xff0c;这两个指针可以同时移动&#…...

react 低代码平台方案汇总

React作为当前最流行的前端框架之一&#xff0c;其生态系统中孕育了多种低代码平台方案&#xff0c;旨在加速应用开发过程。以下是几款基于React的低代码平台或工具&#xff0c;它们通过可视化构建、预制组件、数据绑定等功能&#xff0c;帮助开发者快速构建应用程序&#xff1…...

oss对象上传文件设置格式

PostMapping("upload")ApiOperation(value "上传文件")public Result<UploadDTO> upload(RequestParam("file") MultipartFile file) throws Exception {if (file.isEmpty()) {return new Result<UploadDTO>().error(ModuleErrorCo…...

【Linux学习】进程

下面是有关进程的相关介绍&#xff0c;希望对你有所帮助&#xff01; 小海编程心语录-CSDN博客 目录 1. 进程的概念 1.1 进程与程序 1.2 进程号 2. 进程的状态 2.1 fork创建子进程 2.2 父子进程间的文件共享 3. 进程的诞生与终止 3.1 进程的诞生 3.2 进程的终止 1. 进…...

Python数据分析实验四:数据分析综合应用开发

目录 一、实验目的与要求二、主要实验过程1、加载数据集2、数据预处理3、划分数据集4、创建模型估计器5、模型拟合6、模型性能评估 三、主要程序清单和运行结果四、实验体会 一、实验目的与要求 1、目的&#xff1a; 综合运用所学知识&#xff0c;选取有实际背景的应用问题进行…...

基于51单片机的盆栽自动浇花系统

一.硬件方案 工作原理是湿度传感器将采集到的数据直接传送到ADC0832的IN端作为输入的模拟信号。选用湿度传感器和AD转换&#xff0c;电路内部包含有湿度采集、AD转换、单片机译码显示等功能。单片机需要采集数据时&#xff0c;发出指令启动A/D转换器工作&#xff0c;ADC0832根…...

SpirngMVC框架学习笔记(一):SpringMVC基本介绍

1 SpringMVC 特点&概述 SpringMVC 从易用性&#xff0c;效率上 比曾经流行的 Struts2 更好 SpringMVC 是 WEB 层框架&#xff0c;接管了 Web 层组件, 比如控制器, 视图, 视图解析, 返回给用户的数据格式, 同时支持 MVC 的开发模式/开发架构SpringMVC 通过注解&#xff0c;…...

实现信号发生控制

1. 信号发生器的基本原理 信号发生器是一种能够产生特定波形和频率的电子设备&#xff0c;常用于模拟信号的产生和测试。 信号发生器的基本原理 信号发生器的工作原理基于不同的技术&#xff0c;但最常见的类型包括模拟信号发生器和数字信号发生器&#xff08;DDS&#xff0…...

二叉树基于队列实现的操作详解

一、队列知识补充 有关队列的知识请详见博主的另一篇博客&#xff1a;http://t.csdnimg.cn/3PwO4 本文仅仅附上需要的队列操作供读者参考 //结构体定义 typedef struct BinaryTreeNode* QDataType;typedef struct QueueNode {struct QueueNode* next;QDataType val; }QNode;…...

LabVIEW常用开发架构有哪些

LabVIEW常用开发架构有多种&#xff0c;每种架构都有其独特的特点和适用场合。以下是几种常用的开发架构及其特点和适用场合&#xff1a; 1. 单循环架构 特点&#xff1a; 简单易用适用于小型应用将所有代码放在一个循环中 适用场合&#xff1a; 简单的数据采集和处理任务…...

告别 Dart 中的 Future.wait([])

作为 Dart 开发人员&#xff0c;我们对异步编程和 Futures 的强大功能并不陌生。过去&#xff0c;当我们需要同时等待多个 future 时&#xff0c;我们依赖 Future.wait([]) 方法&#xff0c;该方法返回一个 List<T>。然而&#xff0c;这种方法有一个显着的缺点&#xff1…...

Cisco ASA防火墙抓包命令Capture

在日常运维中&#xff0c;遇到故障时经常需要在ASA上抓包进行诊断。 从抓包中可以看到流量是否经过ASA流量是否被ASA放行&#xff0c;或block&#xff0c;匹配的哪一条ACL capture在Firepower平台上同样适用&#xff0c;无论跑的是ASA还是FTD 1 抓包命令 capture 2 配置方…...

Linux网络编程:HTTP协议

前言&#xff1a; 我们知道OSI模型上层分为应用层、会话层和表示层&#xff0c;我们接下来要讲的是主流的应用层协议HTTP&#xff0c;为什么需要这个协议呢&#xff0c;因为在应用层由于操作系统的不同、开发人员使用的语言类型不同&#xff0c;当我们在传输结构化数据时&…...

HTTP 协议中 GET 和 POST 有什么区别?分别适用于什么场景?

HTTP 协议中 GET 和 POST 是两种常用的请求方法&#xff0c;它们的区别如下: 1. 参数传递方式不同 GET 请求参数是在 URL 中以键值对的形式传递的&#xff0c;例如:http://www.example.com/&#xff1f;key1value1&k ey2value2。 而 POST 请求参数是在请求体中以键值对的…...

talib 安装

这里写自定义目录标题 talib 安装出错 talib 安装出错 https://github.com/cgohlke/talib-build/releases 这里找到轮子 直接装。...

echarts-树图、关系图、桑基图、日历图

树图 树图主要用来表达关系结构。 树图的端点也收symbol的调节 树图的特有属性&#xff1a; 树图的方向&#xff1a; layout、orient子节点收起展开&#xff1a;initialTreeDepth、expandAndCollapse叶子节点设置&#xff1a; leaves操作设置&#xff1a;roam线条&#xff1a…...

04Django项目基本运行逻辑及模板资源套用

对应视频链接点击直达 Django项目用户管理及模板资源 对应视频链接点击直达1.基本运行逻辑Django的基本运行路线&#xff1a;视图views.py中的 纯操作、数据返回、页面渲染 2.模版套用1.寻找一个好的模版2.模板部署--修改适配联动 OVER&#xff0c;不会有人不会吧不会的加Q1394…...

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站&#xff0c;会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后&#xff0c;网站没有变化的情况。 不熟悉siteground主机的新手&#xff0c;遇到这个问题&#xff0c;就很抓狂&#xff0c;明明是哪都没操作错误&#x…...

【Linux】C语言执行shell指令

在C语言中执行Shell指令 在C语言中&#xff0c;有几种方法可以执行Shell指令&#xff1a; 1. 使用system()函数 这是最简单的方法&#xff0c;包含在stdlib.h头文件中&#xff1a; #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...

定时器任务——若依源码分析

分析util包下面的工具类schedule utils&#xff1a; ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类&#xff0c;封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz&#xff0c;先构建任务的 JobD…...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)

设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile&#xff0c;新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...

vue3 定时器-定义全局方法 vue+ts

1.创建ts文件 路径&#xff1a;src/utils/timer.ts 完整代码&#xff1a; import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...

WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)

一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解&#xff0c;适合用作学习或写简历项目背景说明。 &#x1f9e0; 一、概念简介&#xff1a;Solidity 合约开发 Solidity 是一种专门为 以太坊&#xff08;Ethereum&#xff09;平台编写智能合约的高级编…...

AI病理诊断七剑下天山,医疗未来触手可及

一、病理诊断困局&#xff1a;刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断"&#xff0c;医生需通过显微镜观察组织切片&#xff0c;在细胞迷宫中捕捉癌变信号。某省病理质控报告显示&#xff0c;基层医院误诊率达12%-15%&#xff0c;专家会诊…...

在Mathematica中实现Newton-Raphson迭代的收敛时间算法(一般三次多项式)

考察一般的三次多项式&#xff0c;以r为参数&#xff1a; p[z_, r_] : z^3 (r - 1) z - r; roots[r_] : z /. Solve[p[z, r] 0, z]&#xff1b; 此多项式的根为&#xff1a; 尽管看起来这个多项式是特殊的&#xff0c;其实一般的三次多项式都是可以通过线性变换化为这个形式…...

PHP 8.5 即将发布:管道操作符、强力调试

前不久&#xff0c;PHP宣布了即将在 2025 年 11 月 20 日 正式发布的 PHP 8.5&#xff01;作为 PHP 语言的又一次重要迭代&#xff0c;PHP 8.5 承诺带来一系列旨在提升代码可读性、健壮性以及开发者效率的改进。而更令人兴奋的是&#xff0c;借助强大的本地开发环境 ServBay&am…...

pikachu靶场通关笔记19 SQL注入02-字符型注入(GET)

目录 一、SQL注入 二、字符型SQL注入 三、字符型注入与数字型注入 四、源码分析 五、渗透实战 1、渗透准备 2、SQL注入探测 &#xff08;1&#xff09;输入单引号 &#xff08;2&#xff09;万能注入语句 3、获取回显列orderby 4、获取数据库名database 5、获取表名…...