linux驱动开发-内核异步通知

驱动/内核异步通知

在Linux驱动程序中,

异步通知机制允许内核模块在特定事件发生时主动通知用户空间进程。

这样的设计通常用于管理输入输出操作,

确保用户程序可以及时获得数据或状态变化而无需频繁查询设备状态。


在Linux驱动中,常见的异步通知机制主要有以下几种:信号(Signals):通过信号机制,内核可以向用户空间的进程发送通知(如SIGIO)。
等待队列(Wait Queues):允许进程在条件未满足时进入睡眠状态,而在条件满足时被唤醒。
文件描述符的事件通知(non-blocking I/O 和 poll):通过重载poll或select等操作,使得用户空间可以通过轮询的方式获知I/O事件的发生。

使用异步通知的好处

效率:通过非阻塞I/O和轮询的方法,可以有效降低系统开销。

实时性:驱动程序可以在数据可用时立即通知用户进程,减少等待时间。

简化操作:用户空间不需要轮询检查设备状态,可以直接过来响应事件。

示例

#include <linux/module.h>       // 包含模块的基本定义
#include <linux/kernel.h>       // 包含内核相关的基本函数
#include <linux/fs.h>           // 包含文件操作相关的结构体与函数
#include <linux/cdev.h>         // 包含字符设备的相关操作
#include <linux/uaccess.h>      // 用户空间与内核空间数据交换的函数
#include <linux/signal.h>       // 信号相关的定义与函数
#include <linux/sched.h>        // 进程管理相关的结构体与函数#define DEVICE_NAME "mydevice"  // 设备名称
#define CLASS_NAME "myclass"    // 设备类名称static int major_number;  // 主设备号
static struct class* myclass = NULL;  // 设备类结构体
static struct device* mydevice = NULL;  // 设备结构体
static struct cdev mycdev;  // 字符设备结构体
static struct fasync_struct *fasync_queue = NULL;  // 异步通知队列// 设备打开函数
static int mydevice_open(struct inode *inode, struct file *file) {printk(KERN_INFO "mydevice: device opened\n");  // 打开设备时打印信息return 0;  // 成功打开设备
}// 设备关闭函数
static int mydevice_release(struct inode *inode, struct file *file) {printk(KERN_INFO "mydevice: device closed\n");  // 关闭设备时打印信息return 0;  // 成功关闭设备
}// 设备读取函数
static ssize_t mydevice_read(struct file *file, char __user *buffer, size_t length, loff_t *offset) {printk(KERN_INFO "mydevice: read operation\n");  // 读取设备时打印信息return 0;  // 在此示例中,读取操作不返回任何数据
}// 设备写入函数
static ssize_t mydevice_write(struct file *file, const char __user *buffer, size_t length, loff_t *offset) {printk(KERN_INFO "mydevice: write operation\n");  // 写入设备时打印信息// 模拟设备状态变化,发送SIGIO信号给异步通知队列if (fasync_queue) {// 发送异步通知信号//@param fasync_queue: 异步通知队列// @param signal: 信号// @param state: 状态kill_fasync(&fasync_queue, SIGIO, POLL_IN);  // 发送异步通知信号}return length;  // 返回写入的数据长度
}// 异步通知设置函数
//@param fd: 文件描述符
//@param filp: 文件指针
//@param on: 注册或注销标志
//@param fasync_queue: 异步通知队列
// 返回值: 成功返回0,失败返回-EIO
static int mydevice_fasync(int fd, struct file *filp, int on) {// 注册或注销异步通知//@todo: 实现异步通知//@param fd: 文件描述符//@param filp: 文件指针//@param on: 注册或注销标志//@param fasync_queue: 异步通知队列// 返回值: 成功返回0,失败返回-EIOif (fasync_helper(fd, filp, on, &fasync_queue) >= 0) {  // 设置异步通知printk(KERN_INFO "mydevice: fasync setup successful\n");  // 设置成功时打印信息return 0;  // 成功}return -EIO;  // 设置失败,返回错误
}// 文件操作结构体
static struct file_operations fops = {.owner = THIS_MODULE,       // 模块所有者.open = mydevice_open,      // 打开设备的函数指针.release = mydevice_release, // 关闭设备的函数指针.read = mydevice_read,      // 读取设备的函数指针.write = mydevice_write,    // 写入设备的函数指针.fasync = mydevice_fasync,  // 异步通知函数的指针
};// 模块初始化函数
static int __init mydevice_init(void) {// 注册字符设备// @param major_number: 主设备号// @param DEVICE_NAME: 设备名称// @param fops: 文件操作结构体// 返回值: 成功返回主设备号,失败返回错误号major_number = register_chrdev(0, DEVICE_NAME, &fops);  // 注册字符设备if (major_number < 0) {printk(KERN_ALERT "mydevice: failed to register a major number\n");  // 注册失败打印警告return major_number;  // 返回错误号}printk(KERN_INFO "mydevice: registered correctly with major number %d\n", major_number);  // 打印注册成功信息// 创建设备类// @param THIS_MODULE: 当前模块// @param CLASS_NAME: 设备类名称// 返回值: 成功返回设备类结构体,失败返回错误号myclass = class_create(THIS_MODULE, CLASS_NAME);  // 创建设备类if (IS_ERR(myclass)) {// 销毁设备类unregister_chrdev(major_number, DEVICE_NAME);  // 注销设备号printk(KERN_ALERT "mydevice: failed to register device class\n");return PTR_ERR(myclass);  // 返回错误号}printk(KERN_INFO "mydevice: device class registered correctly\n");  // 打印设备类注册成功信息// 创建设备// @param myclass: 设备类结构体// @param NULL: 父设备// @param MKDEV(major_number, 0): 设备号// @param NULL: 设备参数// @param DEVICE_NAME: 设备名称// 返回值: 成功返回设备结构体,失败返回错误号mydevice = device_create(myclass, NULL, MKDEV(major_number, 0), NULL, DEVICE_NAME);  // 创建设备if (IS_ERR(mydevice)) {class_destroy(myclass);  // 销毁设备类unregister_chrdev(major_number, DEVICE_NAME);  // 注销设备号printk(KERN_ALERT "mydevice: failed to create the device\n");return PTR_ERR(mydevice);  // 返回错误号}printk(KERN_INFO "mydevice: device class created correctly\n");  // 打印设备创建成功信息// 初始化字符设备// @param &mycdev: 字符设备结构体// @param &fops: 文件操作结构体cdev_init(&mycdev, &fops);  // 初始化字符设备mycdev.owner = THIS_MODULE;  // 设置设备所有者// 添加字符设备// @param &mycdev: 字符设备结构体// @param MKDEV(major_number, 0): 设备号// @param 1: 设备数量cdev_add(&mycdev, MKDEV(major_number, 0), 1);  // 添加字符设备return 0;  // 初始化成功
}// 模块退出函数
static void __exit mydevice_exit(void) {if (fasync_queue) {fasync_helper(-1, NULL, 0, &fasync_queue);  // 清除异步通知队列}device_destroy(myclass, MKDEV(major_number, 0));  // 销毁设备class_unregister(myclass);  // 注销设备类class_destroy(myclass);  // 销毁设备类unregister_chrdev(major_number, DEVICE_NAME);  // 注销字符设备printk(KERN_INFO "mydevice: Goodbye from the LKM!\n");  // 模块退出时打印信息
}module_init(mydevice_init);  // 模块初始化入口
module_exit(mydevice_exit);  // 模块退出入口MODULE_LICENSE("GPL");  // 模块许可证
MODULE_AUTHOR("gopher");  // 模块作者
MODULE_DESCRIPTION("A simple character device driver with async notification");  // 模块描述
MODULE_VERSION("1.0");  // 模块版本

测试

#include <stdio.h>       // 包含标准输入输出库
#include <stdlib.h>      // 包含标准库,提供通用的函数
#include <fcntl.h>       // 包含文件控制相关的定义
#include <unistd.h>      // 包含与Unix标准的函数
#include <signal.h>      // 包含信号处理相关的定义
#include <string.h>      // 包含字符串处理相关的函数#define DEVICE_FILE "/dev/mydevice"  // 定义设备文件路径// 信号处理函数
void sigio_handler(int signo) {// 当接收到SIGIO信号时调用该函数if (signo == SIGIO) {  // 检查信号是否为SIGIOprintf("Received SIGIO signal\n");  // 打印接收到的信号信息}
}int main() {int fd;  // 文件描述符int flags;  // 文件状态标志// 设置SIGIO信号处理函数// @note: 信号处理函数的第一个参数是信号值,第二个参数是信号处理函数的指针// @note: 这里将信号处理函数设置为sigio_handlersignal(SIGIO, sigio_handler);  // 设置信号处理函数,当接收到SIGIO信号时,调用sigio_handler// 打开设备文件,进行读写操作fd = open(DEVICE_FILE, O_RDWR);  if (fd == -1) {  // 检查打开是否成功perror("Failed to open the device");  // 如果失败,打印错误信息return -1;  // 返回错误代码}// 设置当前进程为文件描述符的拥有者,以接收信号fcntl(fd, F_SETOWN, getpid());  // 获取当前文件描述符的状态标志//@note: F_GETFL获取文件状态标志,F_SETFL设置文件状态标志// 这里设置文件描述符为异步通知模式,以便接收SIGIO信号flags = fcntl(fd, F_GETFL);// 设置文件描述符为异步通知模式,以便接收SIGIO信号// @note: FASYNC表示异步通知模式,F_SETFL设置文件状态标志// @note: 这里使用位或运算符“|”将FASYNC与flags进行或运算,得到新的flags值// @note: 新的flags值将会应用到文件描述符上,从而使得文件描述符变为异步通知模式// @note: 异步通知模式下,文件描述符上发生的事件(如读写等)将会触发SIGIO信号// @note: 信号处理函数sigio_handler将会被调用,处理异步通知事件fcntl(fd, F_SETFL, flags | FASYNC);  printf("Waiting for SIGIO signal...\n");  // 提示用户正在等待SIGIO信号while (1) {  // 主循环sleep(1);  // 每次循环休眠1秒}close(fd);  // 关闭设备文件(这部分实际上是不会执行到的,因为上面的循环是无限的)return 0;  // 返回0,表示程序正常退出
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/145701.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

C++ 9.20

练习&#xff1a;定义一个矩形类&#xff08;Rectangle&#xff09;&#xff0c;包含私有成员宽度&#xff08;width&#xff09;、高度&#xff08;height&#xff09; 包含公共成员函数&#xff1a; 初始化矩形&#xff08;init&#xff09; 设置宽度&#xff08;set_w&am…

给儿童掏耳朵用哪个好?儿童耳勺最建议买的五个牌子

儿童的耳朵清洁是家长最烦恼的事情之一&#xff0c;近年来传统耳勺出现的意外新闻颇多&#xff0c;棉签等工具的表面粗糙&#xff0c;稍不注意就会刮伤儿童脆弱的耳道肌肤&#xff0c;那么除了这些以外&#xff0c;给儿童掏耳朵用哪个好&#xff1f; 小编建议家长都入一个可视挖…

唤醒数据中台潜力,加速数据飞轮转动:数据驱动秘籍

在这个数据爆炸的时代&#xff0c;企业的数据资产正变得越来越重要。然而&#xff0c;收集和存储数据只是数据驱动旅程的第一步。如何唤醒这些沉睡的数据&#xff0c;真正让它们为业务服务&#xff1f; 这才是企业成功的关键。 数据中台曾被视为整合企业内外数据资源的利器&am…

javascript 3 个有序点的方向(Orientation of 3 ordered points)

给定三个点 p1、p2 和 p3&#xff0c;任务是确定这三个点的方向。 平面中有序三重点的方向可以是 逆时针 顺时针 共线 下图显示了 (a,b,c) 的不同可能方向 如果 (p1, p2, p3) 的方向共线&#xff0c;则 (p3, p2, p1) 的方向也共线。 如果 (p1, p2, p3) 的方向是顺时针&a…

Python GUI 编程:tkinter 初学者入门指南——窗口

目录&#xff1a; 创建窗口更改窗口标题更改窗口大小和位置窗口在屏幕上居中窗口设置的其他属性 Tkinter 是在 Python 中开发 GUI&#xff08;图形用户界面&#xff09;最常用的库。在本指南中&#xff0c;我们将引导您了解 Tkinter 的基本知识&#xff0c;学习如何使用 Tkinte…

Vue3:自定义事件实现组件通信

目录 一.性质 1.双向通信 2.灵活性 3.传参能力 4.声明机制 5.事件验证 6.修饰符支持 7.响应式更新 8.解耦组件 9.易于测试 10.性能优化 二.使用 1.父组件 2.子组件 三.代码 1.父组件代码 2.子组件代码 四.效果 在Vue3中&#xff0c;自定义事件是实现组件间通…

NLP(二)-文本表示

One-hot One-hot&#xff08;独热&#xff09;编码是一种最简单的文本表示方式。如果有一个大小为V的词表&#xff0c;对于第i个词$w_i$&#xff0c;可以用一个长度为V的向量来表示&#xff0c;其中第i个元素为1&#xff0c;其它为0.例如&#xff1a; 减肥&#xff1a;[1, 0,…

C++11之统一的列表初始化

一.{}初始化 在c98中&#xff0c;标准允许使用{}对数组或结构体元素进行统一的列表初始值设定&#xff1a; struct mess {int _x;string _str; }; int main() {//注意&#xff0c;使用new的一定是指针int* arr new int[4] {1, 2, 3, 4};//数组初始化int arr[] { 1,3,5,6 };…

深度学习激活函数

激活函数是神经网络模型重要的组成部分&#xff0c;本文作者Sukanya Bag从激活函数的数学原理出发&#xff0c;详解了十种激活函数的优缺点。 激活函数&#xff08;Activation Function&#xff09;是一种添加到人工神经网络中的函数&#xff0c;旨在帮助网络学习数据中的复杂模…

linux之nacos安装

1:下载nacos安装包 方式一、进入官网下载压缩包 官网地址 找到nacos-server-2.0.1.tar.gz 点击进行下载&#xff0c;下载完成后上传到服务器中。 方式二、使用wget命令下载 也有两种方式&#xff1a;第一种下载速度较慢 wget https://github.com/alibaba/nacos/releases/downl…

圆柱包围框-Bounding Cylinder-原理-代码实现

定义&#xff1a;使用一个圆柱体包围点云的所有点&#xff0c;通常用于长柱状物体。 优点&#xff1a;适合于柱状或长条形的点云。 缺点&#xff1a;计算较为复杂&#xff0c;尤其是确定圆柱体的轴线方向和半径。 找到圆柱尽量满足下面条件 找到能够完全包围3D物体的最小圆柱…

户外无线麦克风哪个牌子好,降噪麦克风哪个牌子好,领夹麦推荐

对于热爱记录与户外直播的自媒体人来说&#xff0c;一款高性能的无线领夹麦克风决定了音频的质量。市场上虽有品牌如大疆、罗德、西圣等凭借技术创新引领潮流&#xff0c;但同时也存在一些产品&#xff0c;因设计缺陷在运动时声音捕捉不稳定。作为运动爱好者与音频设备测评师&a…

网络资源模板--Android Studio 图书借阅App

目录 一、项目演示 二、项目测试环境 三、项目详情 四、完整的项目源码 一、项目演示 网络资源模板--图书借阅App 二、项目测试环境 三、项目详情 首页 这段代码是一个 Android 应用的 MainActivity 类&#xff0c;功能简要总结如下&#xff1a; 1. **界面设置**&#xf…

数据结构不再难懂:带你轻松搞定图

数据结构入门学习&#xff08;全是干货&#xff09;——图 1 图 1.1 什么是图 图是一种用于表示多对多关系的数学模型。它由一组顶点和一组边构成&#xff0c;用于描述事物之间的复杂关联。 顶点&#xff1a;通常用 V (Vertex) 表示&#xff0c;代表事物或对象。边&#xf…

2024华为杯研赛E题保姆级教程思路分析

E题题目&#xff1a;高速公路应急车道紧急启用模型 今年的E题设计到图像/视频处理&#xff0c;实际上&#xff0c;E题的难度相对来说较低&#xff0c;大家不用畏惧视频的处理&#xff0c;被这个吓到。实际上&#xff0c;这个不难&#xff0c;解决了视频的处理问题&#xff0c;…

华为---代理ARP工作过程示例分析

目录 1. 示例场景 2. 基本配置 3. 配置代码 4. 测试验证 5. 抓包分析 5.1 在代理ARP环境下PC1和PC2通信分析 5.2 取消代理ARP环境下PC1和PC2通信分析 【1】取消R1路由器GE 0/0/1端口ARP代理 【2】取消R2路由器GE 0/0/1端口ARP代理 1. 示例场景 如上图所示&#xff0c;…

windows环境下配置MySQL主从启动失败 查看data文件夹中.err发现报错unknown variable ‘log‐bin=mysql‐bin‘

文章目录 问题解决方法 问题 今天在windows环境下配置MySQL主从同步&#xff0c;在修改my.ini文件后发现MySQL启动失败了 打开my.ini检查参数发现没有问题 [mysqld] #开启二进制日志&#xff0c;记录了所有更改数据库数据的SQL语句 log‐bin mysql‐bin #设置服务id&#x…

java重点学习-总结

十五 总结 https://kdocs.cn/l/crbMWc8xEZda &#xff08;总结全部的精华&#xff09; 1.面试准备 企业筛选简历规则简历编写注意事项(亮点)项目怎么找&#xff0c;学习到什么程度面试过程(表达结构、什么样的心态去找工作) 2.redis 缓存相关(缓存击穿、穿透、雪崩、缓存过期淘…

农业电商服务系统小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;会员管理&#xff0c;商家管理&#xff0c;商品分类管理&#xff0c;商品信息管理&#xff0c;农产品监督管理&#xff0c;助农信息管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统首页…

使用Renesas R7FA8D1BH (Cortex®-M85)实现多功能UI

目录 概述 1 系统框架介绍 1.1 模块功能介绍 1.2 UI页面功能 2 软件框架结构实现 2.1 软件框架图 2.1.1 应用层API 2.1.2 硬件驱动层 2.1.3 MCU底层驱动 2.2 软件流程图 4 软件功能实现 4.1 状态机功能核心代码 4.2 页面功能函数 4.3 源代码文件 5 功能测试 5.1…