字符设备驱动第八课----异步通知(信号驱动IO)

2024-05-09 20:08

本文主要是介绍字符设备驱动第八课----异步通知(信号驱动IO),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

概述

类比运用程序中的kill-----signal,在运用程序中常常一个进程用kill(pid,sig)向另一
进程发信号,另一个进程用signal(sig,handler)绑定相应的处理函数,实现了异步通知。今天要讲的就是:运用程序要读,但它并不知道啥时候有东西可读,用read()
阻塞去读显然效率不高,read()配合IO多路复用非阻塞一直在那里轮询的话效率也不好。
这里采用的办法是:驱动层有数据可读的时候kill一个SIGIO信号给运用层,
运用层收到SIGIO信号后调用预先绑定好的处理函数把数据读走。

若还迷糊概念,请看看这位大神的清晰讲解:
信号驱动IO与异步通知

<include/linux/fs.h>struct file_operations {int (*open) (struct inode *, struct file *);int (*flush) (struct file *, fl_owner_t id);int (*release) (struct inode *, struct file *);int (*fsync) (struct file *, loff_t, loff_t, int datasync);       //用于异步通知...
}
<include/linux/fs.h>struct fasync_struct {spinlock_t      fa_lock;int         magic;int         fa_fd;struct fasync_struct    *fa_next; /* singly linked list */struct file     *fa_file;struct rcu_head     fa_rcu;
};
/*
* 功能: 得到异步通知结构。根据mod,将异步通知结构体加入链表
* 运用程序端调用这些函数改变标志和owner时就调用了这个底层实现。
*    fcntl(STDIN_FILENO,F_SETOWN,getpid());
*    oflags = fcntl(STDIN_FILENO,F_GETFL);
*    fctcl(STDIN_FILENO,F_SETFL,oflags | FASYNC);
* 输入参数:  fd:             文件描述符
*          filp:          file结构体指针
* 输出参数:fapp:得到的异步通知结构体          
*/
int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)
/** 功能:通过异步通知结构发信号,此信号发出时运用端就会收到SGIO信号,就会回调预先绑定的处理函数。* 参数:struct fasync_struct **fp:  异步通知结构*      int signo:                  信号(SIGIO)*      int events:                 事件:POLLIN、POLLOUT*/
void kill_fasync(struct fasync_struct **fp, int sig, int band)

范例:

1.驱动端:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/errno.h>#include <asm/current.h>
#include <linux/sched.h>#include <linux/uaccess.h>
#include <linux/poll.h>#include <asm/atomic.h>
#include <linux/mutex.h>#include <linux/wait.h>#include <linux/device.h>
static struct class *cls = NULL;static int major = 0;
static int minor = 0;
const  int count = 6;#define DEVNAME "demo"static struct cdev *demop = NULL;
static atomic_t tv;
static wait_queue_head_t wq;static struct fasync_struct *fasync = NULL;//定义异步通知结构体#define KMAX 1024
static char kbuf[KMAX];
static int counter = 0;//打开设备
static int demo_open(struct inode *inode, struct file *filp)
{//get major and minor from inodeprintk(KERN_INFO "(major=%d, minor=%d), %s : %s : %d\n",imajor(inode), iminor(inode), __FILE__, __func__, __LINE__);if(!atomic_dec_and_test(&tv)){atomic_inc(&tv);return -EBUSY;}memset(kbuf, 0, KMAX);counter = 0;return 0;
}//关闭设备
static int demo_release(struct inode *inode, struct file *filp)
{//get major and minor from inodeprintk(KERN_INFO "(major=%d, minor=%d), %s : %s : %d\n",imajor(inode), iminor(inode), __FILE__, __func__, __LINE__);atomic_inc(&tv);return 0;
}//读设备
//ssize_t read(int fd, void *buf, size_t count)
static ssize_t demo_read(struct file *filp, char __user *buf, size_t size, loff_t *offset)
{int err = 0;struct inode *inode = filp->f_path.dentry->d_inode;  //获取文件的inod号//get major and minor from inodeprintk(KERN_INFO "(major=%d, minor=%d), %s : %s : %d\n",imajor(inode), iminor(inode), __FILE__, __func__, __LINE__);if(!counter){if(filp->f_flags & O_NONBLOCK){return -EAGAIN;}err = wait_event_interruptible(wq, (0 != counter));//睡在条件上的等待队列if(err){                                            //没东西可读,睡return err;}}if(counter < size){size = counter;}if(copy_to_user(buf, kbuf, size)){return -EAGAIN;}counter = 0;return size;
}//写设备
static ssize_t demo_write(struct file *filp, const char __user *buf, size_t size, loff_t *offset)
{struct inode *inode = filp->f_path.dentry->d_inode;//get major and minor from inodeprintk(KERN_INFO "(major=%d, minor=%d), %s : %s : %d\n",imajor(inode), iminor(inode), __FILE__, __func__, __LINE__);if(size > KMAX){return -ENOMEM;}if(copy_from_user(kbuf, buf, size)){return -EAGAIN;}counter = size;wake_up_interruptible(&wq);//广播唤醒等待队列kill_fasync(&fasync, SIGIO, POLLIN);//向fasync结构体发信号,//与fasync关联的进程(通过fcntl(...,pid)系列函数关联)就会收到SIGIO信号return size;
}/* IO多路复用支持*/
static unsigned int demo_poll(struct file *filp, struct poll_table_struct *pts)
{unsigned int mask = 0;struct inode *inode = filp->f_path.dentry->d_inode;//get major and minor from inodeprintk(KERN_INFO "(major=%d, minor=%d), %s : %s : %d\n",imajor(inode), iminor(inode), __FILE__, __func__, __LINE__);poll_wait(filp, &wq, pts);//io多路复用支持,只有等待队列中有就绪的事件才会往下走,否则阻塞if(counter){mask = (POLLIN | POLLRDNORM);//返回,告诉运用层的poll函数:就绪事件是输入事件}return mask;
}/*异步通知接口函数,应用层调fcntl()时调到此函数*/
static int demo_fasync(int fd, struct file *filp, int mode)
{struct inode *inode = filp->f_path.dentry->d_inode;//get major and minor from inodeprintk(KERN_INFO "(major=%d, minor=%d), %s : %s : %d\n",imajor(inode), iminor(inode), __FILE__, __func__, __LINE__);return fasync_helper(fd, filp, mode, &fasync);//根据mod,将异步通知结构体加入链表//或者从链表中移除。得到信息并填充到fasync结构体中
}static struct file_operations fops = {.owner  = THIS_MODULE,.open   = demo_open,.release= demo_release,.read   = demo_read,.write  = demo_write,.poll   = demo_poll,.fasync = demo_fasync,
};static int __init demo_init(void)
{dev_t devnum;int ret, i;struct device *devp = NULL;//get command and pidprintk(KERN_INFO "(%s:pid=%d), %s : %s : %d\n",current->comm, current->pid, __FILE__, __func__, __LINE__);//1. alloc cdev objdemop = cdev_alloc();if(NULL == demop){return -ENOMEM;}//2. init cdev objcdev_init(demop, &fops);ret = alloc_chrdev_region(&devnum, minor, count, DEVNAME);if(ret){goto ERR_STEP;}major = MAJOR(devnum);//3. register cdev objret = cdev_add(demop, devnum, count);if(ret){goto ERR_STEP1;}cls = class_create(THIS_MODULE, DEVNAME);if(IS_ERR(cls)){ret = PTR_ERR(cls);goto ERR_STEP1;}for(i = minor; i < (count+minor); i++){devp = device_create(cls, NULL, MKDEV(major, i), NULL, "%s%d", DEVNAME, i);if(IS_ERR(devp)){ret = PTR_ERR(devp);goto ERR_STEP2;}}// init atomic_tatomic_set(&tv, 1);init_waitqueue_head(&wq);//初始化等待队列//get command and pidprintk(KERN_INFO "(%s:pid=%d), %s : %s : %d - ok.\n",current->comm, current->pid, __FILE__, __func__, __LINE__);return 0;ERR_STEP2:for(--i; i >= minor; i--){device_destroy(cls, MKDEV(major, i));}class_destroy(cls);ERR_STEP1:unregister_chrdev_region(devnum, count);ERR_STEP:cdev_del(demop);//get command and pidprintk(KERN_INFO "(%s:pid=%d), %s : %s : %d - fail.\n",current->comm, current->pid, __FILE__, __func__, __LINE__);return ret;
}static void __exit demo_exit(void)
{int i;//get command and pidprintk(KERN_INFO "(%s:pid=%d), %s : %s : %d - leave.\n",current->comm, current->pid, __FILE__, __func__, __LINE__);for(i=minor; i < (count+minor); i++){device_destroy(cls, MKDEV(major, i));}class_destroy(cls);unregister_chrdev_region(MKDEV(major, minor), count);cdev_del(demop);
}module_init(demo_init);
module_exit(demo_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("Farsight");
MODULE_DESCRIPTION("Demo for kernel module");

2.运用程序端:

#include <stdio.h>
#include <string.h>#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>#include <poll.h>
#include <signal.h>#include <errno.h>int fd = -1;void handler(int sig)//信号处理函数,与signal绑定的
{struct pollfd pfd = {.fd = fd,.events = POLLIN,};int ret = poll(&pfd, 1, ~0);//监控pfd,最大文件描述符1,永不超时if(0 >= ret){               //没一个就绪则阻塞,只要其中有任意一个就绪就往下走perror("poll");return;}#define MAX 1024char buf[MAX];memset(buf, 0, MAX); if(0 > read(fd, buf, MAX)){perror("read");}else{printf("RD: %s\n", buf);}
}int main(int num, char *argv[])
{if(2 != num){printf("Usage: %s /dev/devfile\n", argv[0]);return -1;}fd = open(argv[1], O_RDWR|O_NONBLOCK);if(0 > fd){printf("pid = %d, %s\n", getpid(), (char *)strerror(errno));return -1;}signal(SIGIO, handler);//绑定信号处理函数fcntl(fd, F_SETOWN, getpid());//关联收发,设置对应文件的拥有者是本进程,这样接下来才能进行信号的收发int flag = fcntl(fd, F_GETFL);//读取对应文件描述符上的flg信息flag  |= O_ASYNC;fcntl(fd, F_SETFL, flag);     //设置对应文件描述符上的flg信息,使其支持异步通知//这个函数实质上最终调用的是操作方法集中的.fasync标准接口,对应到驱动层中的相应函数while(1){printf("---------w: 1----------\n");#define MAX 1024char buf[MAX];fgets(buf, MAX, stdin);         write(fd, buf, strlen(buf));}close(fd);return 0;
}

这篇关于字符设备驱动第八课----异步通知(信号驱动IO)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/974372

相关文章

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后

如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别详解

《如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别详解》:本文主要介绍如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别的相关资料,描述了如何使用海康威视设备网络SD... 目录前言开发流程问题和解决方案dll库加载不到的问题老旧版本sdk不兼容的问题关键实现流程总结前言作为

使用C++将处理后的信号保存为PNG和TIFF格式

《使用C++将处理后的信号保存为PNG和TIFF格式》在信号处理领域,我们常常需要将处理结果以图像的形式保存下来,方便后续分析和展示,C++提供了多种库来处理图像数据,本文将介绍如何使用stb_ima... 目录1. PNG格式保存使用stb_imagephp_write库1.1 安装和包含库1.2 代码解

异步线程traceId如何实现传递

《异步线程traceId如何实现传递》文章介绍了如何在异步请求中传递traceId,通过重写ThreadPoolTaskExecutor的方法和实现TaskDecorator接口来增强线程池,确保异步... 目录前言重写ThreadPoolTaskExecutor中方法线程池增强总结前言在日常问题排查中,

一文详解Java Condition的await和signal等待通知机制

《一文详解JavaCondition的await和signal等待通知机制》这篇文章主要为大家详细介绍了JavaCondition的await和signal等待通知机制的相关知识,文中的示例代码讲... 目录1. Condition的核心方法2. 使用场景与优势3. 使用流程与规范基本模板生产者-消费者示例

微服务架构之使用RabbitMQ进行异步处理方式

《微服务架构之使用RabbitMQ进行异步处理方式》本文介绍了RabbitMQ的基本概念、异步调用处理逻辑、RabbitMQ的基本使用方法以及在SpringBoot项目中使用RabbitMQ解决高并发... 目录一.什么是RabbitMQ?二.异步调用处理逻辑:三.RabbitMQ的基本使用1.安装2.架构

Java 字符数组转字符串的常用方法

《Java字符数组转字符串的常用方法》文章总结了在Java中将字符数组转换为字符串的几种常用方法,包括使用String构造函数、String.valueOf()方法、StringBuilder以及A... 目录1. 使用String构造函数1.1 基本转换方法1.2 注意事项2. 使用String.valu

Go语言使用Buffer实现高性能处理字节和字符

《Go语言使用Buffer实现高性能处理字节和字符》在Go中,bytes.Buffer是一个非常高效的类型,用于处理字节数据的读写操作,本文将详细介绍一下如何使用Buffer实现高性能处理字节和... 目录1. bytes.Buffer 的基本用法1.1. 创建和初始化 Buffer1.2. 使用 Writ

如何使用celery进行异步处理和定时任务(django)

《如何使用celery进行异步处理和定时任务(django)》文章介绍了Celery的基本概念、安装方法、如何使用Celery进行异步任务处理以及如何设置定时任务,通过Celery,可以在Web应用中... 目录一、celery的作用二、安装celery三、使用celery 异步执行任务四、使用celery

无线路由器哪个品牌好用信号强? 口碑最好的三个路由器大比拼

《无线路由器哪个品牌好用信号强?口碑最好的三个路由器大比拼》不同品牌在信号覆盖、稳定性和易用性等方面各有特色,如何在众多选择中找到最适合自己的那款无线路由器呢?今天推荐三款路由器让你的网速起飞... 今天我们来聊聊那些让网速飞起来的路由器。在这个信息爆炸的时代,一个好路由器简直就是家庭网编程络的心脏。无论你