当前位置: 首页 > 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…...

java_网络服务相关_gateway_nacos_feign区别联系

1. spring-cloud-starter-gateway 作用&#xff1a;作为微服务架构的网关&#xff0c;统一入口&#xff0c;处理所有外部请求。 核心能力&#xff1a; 路由转发&#xff08;基于路径、服务名等&#xff09;过滤器&#xff08;鉴权、限流、日志、Header 处理&#xff09;支持负…...

树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频

使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...

Frozen-Flask :将 Flask 应用“冻结”为静态文件

Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是&#xff1a;将一个 Flask Web 应用生成成纯静态 HTML 文件&#xff0c;从而可以部署到静态网站托管服务上&#xff0c;如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)

更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...

是否存在路径(FIFOBB算法)

题目描述 一个具有 n 个顶点e条边的无向图&#xff0c;该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序&#xff0c;确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数&#xff0c;分别表示n 和 e 的值&#xff08;1…...

#Uniapp篇:chrome调试unapp适配

chrome调试设备----使用Android模拟机开发调试移动端页面 Chrome://inspect/#devices MuMu模拟器Edge浏览器&#xff1a;Android原生APP嵌入的H5页面元素定位 chrome://inspect/#devices uniapp单位适配 根路径下 postcss.config.js 需要装这些插件 “postcss”: “^8.5.…...

LLaMA-Factory 微调 Qwen2-VL 进行人脸情感识别(二)

在上一篇文章中,我们详细介绍了如何使用LLaMA-Factory框架对Qwen2-VL大模型进行微调,以实现人脸情感识别的功能。本篇文章将聚焦于微调完成后,如何调用这个模型进行人脸情感识别的具体代码实现,包括详细的步骤和注释。 模型调用步骤 环境准备:确保安装了必要的Python库。…...

Vue3中的computer和watch

computed的写法 在页面中 <div>{{ calcNumber }}</div>script中 写法1 常用 import { computed, ref } from vue; let price ref(100);const priceAdd () > { //函数方法 price 1price.value ; }//计算属性 let calcNumber computed(() > {return ${p…...

uni-app学习笔记三十五--扩展组件的安装和使用

由于内置组件不能满足日常开发需要&#xff0c;uniapp官方也提供了众多的扩展组件供我们使用。由于不是内置组件&#xff0c;需要安装才能使用。 一、安装扩展插件 安装方法&#xff1a; 1.访问uniapp官方文档组件部分&#xff1a;组件使用的入门教程 | uni-app官网 点击左侧…...