Linux设备驱动中的异步通知与异步I/O学习s

news2024/11/16 17:59:02

1、异步通知的概念和作用

        异步通知的意思是:一旦设备就绪,则主动通知应用程序,这样应用程序根本就不需要查询设备状态,这一点非常类似于硬件上”中断“的概念,比较准确的称谓是”信号驱动的异步I/O“。信号是在软件层次上对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是异步的,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。

2、Linux异步通知编程

        Linux信号。使用信号在进程间通信(IPC)是UNIX系统中的一种传统机制,Linux系统也支持这种机制。在Linux系统中,异步通知使用信号来实现,Linux系统中可用的信号及其定义如下图所示。

        除了SIGSTOP和SIGKILL两个信号外,进程能够忽略或捕获其他全部的信号。一个信号被捕获的意思是当一个信号到达时有相应的代码处理它。如果一个信号没有被这个进程捕获,内核将采取默认处理。 


        信号的接收。在用户程序中为了捕获信号,可以使用signal()函数来设置对应信号的处理函数,如下所示,第一个参数指定信号的值,第二个参数指定针对前面信号值的处理函数,若为SIG_IGN,表示忽略该信号;若为SIG_DFL,表示采用系统默认方式处理信号;若位用户自定义的函数,则信号被捕获到后,该函数将被执行。

sighandler_t signal(int signum,sighandler_t handler);

        如果signal()调用成功,它返回最后一次为信号signum绑定的处理函数handler值,失败返回SIG_ERR。以下是一个使用信号实现异步通知的例子:

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#define MAX_LEN 100
void input_handler(int num)
{
    char data[MAX_LEN];
    int len;

    len = read(STDIN_FILENO,&data,MAX_LEN);
    data[len]=0;
    printf("input available:%s\n",data);
}
int main()
{
    int oflags;

    signal(SIGIO,input_handler);
    fcntl(STDIN_FILENO,F_SETOWN,getpid());//设置本进程为标准输入文件的拥有者,指示信号要发送给本进程
    //oflags = fcntl(STDIN_FILENO,F_GETFL);
    //fcntl(STDIN_FILENO,F_SETFL,oflags | FASYNC);
    while(1);
}

 在用户空间中能处理一个设备释放的信号必须完成以下工作:

1、通过F_SETOWN IO控制命令设置设备文件的拥有者为本进程,这样从设备驱动发出的信号才能被本进程接收到。

2、通过F_SETFL IO控制命令设置设备文件支持FASYNC,即异步通知模式。

3、通过signal()函数连接信号和信号处理函数。


        信号的释放。在设备驱动和应用程序的异步通知交互中,仅仅在应用程序端捕获信号是不够的,应该在合适的时机让设备驱动释放信号,在设备驱动程序中增加信号释放的相关代码。

        设备驱动中的异步通知编程比较简单,主要用到一项数据结构和两个函数。数据结构是fasync_struct结构体,两个函数如下:

处理FASYNC标志变更的函数:

int fasync_helper(int fd,struct file *filp,int mode,struct fasync_struct **fa);

释放信号用的函数:

void kill_fasync(struct fasync_struct **fa,int sig,int band);

和其他的设备驱动一样,fasync_struct结构体可以放在设备结构体中:

struct xxx_dev{
    struct cdev cdev;
    ......
    struct fasync_struct *async_queue;//异步结构体指针
};

以下是支持异步通知的设备驱动程序fasync()函数的模板:

static int xxx_fasync(int fd,struct file *filp,int mode)
{
    struct xxx_dev *dev = filp->private_data;
    return fasync_helper(fd,filp,mode,&dev->async_queue);
}

在设备资源可获得时,应该调用kill_fasync()释放SIGIO信号,可读时第3个参数设置为POLL_IN,可写时第3个参数设置为POLL_OUT,代码如下:

ssize_t globalfifo_write(struct file *filp, const char __user *buf, size_t size, loff_t *offset)
{
    struct globalfifo_dev *dev = filp->private_data;
    ...
    //产生异步信号
    if(dev->async_queue)
    {
        kill_fasync(&dev->async_queue,SIGIO,POLL_IN);
    }
    ...
}

最后在文件关闭时,应在release函数中调用设备驱动的fasync函数将文件从异步通知的队列中删除:

int globalfifo_release(struct inode *node, struct file *filp)
{
    struct globalfifo_dev *dev = filp->private_data;
    ...
    //将文件从异步通知队列中删除
    globalfifo_fasync(-1,filp,0);
    ...
    return 0;
}

        支持异步通知的globalfifo的驱动。需要在file_operations中定义 .fasync函数。当应用程序使用fcntl设置FASYNC标志时,驱动程序就会调用定义好的fasync函数,注册异步通知队列。驱动程序在写完数据后,使用kill_fasync函数释放读信号。

#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/bits.h>
#include <linux/debugfs.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/pmbus.h>
#include <linux/util_macros.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/platform_device.h>
#define GLOBALFIFO_SIZE 20
int globalfifo_major=0;
struct class *globalfifo_class;
struct globalfifo_dev{
    //struct cdev cdev;//cdev结构体
    unsigned int current_len;//fifo有效数据长度
    unsigned char mem[GLOBALFIFO_SIZE];//全局内存123
    struct semaphore sem;//并发控制用的信号量
    wait_queue_head_t r_wait;//阻塞读用的等待队列头
    wait_queue_head_t w_wait;//阻塞写用的等待队列头
    struct fasync_struct *async_queue;//异步结构指针,用于读
};
struct globalfifo_dev *globalfifo_devp;

int globalfifo_open(struct inode *node, struct file *filp)
{
    printk(KERN_INFO"globalfifo_open\n");
    filp->private_data = globalfifo_devp;
    printk(KERN_INFO"private_data\n");
    return 0;
}
int globalfifo_fasync(int fd, struct file *filp, int mode)
{
    struct globalfifo_dev *dev = filp->private_data;
    printk("globalfifo_fasync");
    return fasync_helper(fd,filp,mode,&dev->async_queue);
}
int globalfifo_release(struct inode *node, struct file *filp)
{
    printk(KERN_INFO"globalfifo_release\n");
    globalfifo_fasync(-1,filp,0);
    return 0;
}
ssize_t globalfifo_read(struct file *filp, char *buf, size_t size, loff_t *offset)
{
    printk(KERN_INFO"globalfifo_read\n");
    int ret;
    struct globalfifo_dev *dev = filp->private_data;
    printk(KERN_INFO"read try to declare queue\n");
    DECLARE_WAITQUEUE(wait,current);
    printk(KERN_INFO"read try to get sem\n");
    down(&dev->sem);
    printk(KERN_INFO"read have got sem\n");
    add_wait_queue(&dev->r_wait,&wait);

    if(dev->current_len==0)
    {
        if(filp->f_flags & O_NONBLOCK)
        {
            ret = -EAGAIN;
            goto out;
        }
        __set_current_state(TASK_INTERRUPTIBLE);
        up(&dev->sem);
        printk(KERN_INFO"read release sem,begin shcedule\n");
        schedule();
        if(signal_pending(current))
        {
            ret = -ERESTARTSYS;
            goto out2;
        }
        down(&dev->sem);
    }
    if(size > dev->current_len)
        size = dev->current_len;

    if(copy_to_user(buf,dev->mem,size))
    {
         ret = -EFAULT;
         goto out;
     }
     else
    {
        //printk("%s",kern_buf);
        memcpy(dev->mem,dev->mem+size,dev->current_len-size);
        dev->current_len-=size;
        printk(KERN_INFO"read %d bytes(s),current_len:%d\n",size,dev->current_len);
        wake_up_interruptible(&dev->w_wait);
        ret = size;
    }
    out:up(&dev->sem);
    out2:remove_wait_queue(&dev->r_wait,&wait);
    set_current_state(TASK_RUNNING);
    return size;
}
ssize_t globalfifo_write(struct file *filp, const char __user *buf, size_t size, loff_t *offset)
{
    printk("global_fifo write");
    struct globalfifo_dev *dev = filp->private_data;
    int ret,i=0,j;

    DECLARE_WAITQUEUE(wait,current);
    down(&dev->sem);
    add_wait_queue(&dev->w_wait,&wait);

    if(dev->current_len == GLOBALFIFO_SIZE)
    {
        if(filp->f_flags & O_NONBLOCK)
        {
            ret = -EAGAIN;
            goto out;
        }
        __set_current_state(TASK_INTERRUPTIBLE);
        up(&dev->sem);
        schedule();
        if(signal_pending(current))
        {
            ret = -ERESTARTSYS;
            goto out2;
        }
        down(&dev->sem);
    }
    if(size>GLOBALFIFO_SIZE-dev->current_len)
    {
        size = GLOBALFIFO_SIZE-dev->current_len;
    }
    //for(i=dev->current_len,j=0;i<dev->current_len+size;i++,j++)dev->mem[i]=buf[j];
    if(copy_from_user(dev->mem + dev->current_len,buf,size))
    {
        ret = -EFAULT;
        goto out;
    }
    else
    {
        dev->current_len+=size;
        printk(KERN_INFO"written %d bytes(s),current_len:%d\n",size,dev->current_len);
        wake_up_interruptible(&dev->r_wait);

        //产生异步读信号
        if(dev->async_queue)
        {
            kill_fasync(&dev->async_queue,SIGIO,POLL_IN);
        }
        ret = size;
    }
    out:up(&dev->sem);
    out2:remove_wait_queue(&dev->w_wait,&wait);
    set_current_state(TASK_RUNNING);
    return ret;

}
unsigned int globalfifo_poll(struct file *filp, struct poll_table_struct *wait)
{
    unsigned int mask = 0;
    struct globalfifo_dev *dev = filp->private_data;
    down(&dev->sem);
    poll_wait(filp,&dev->r_wait,wait);
    poll_wait(filp,&dev->w_wait,wait);
    if(dev->current_len != 0)
    {
        mask |= POLLIN | POLLRDNORM;
    }
    if(dev->current_len != GLOBALFIFO_SIZE)
    {
        mask |= POLLOUT | POLLWRNORM;
    }
    up(&dev->sem);
    return mask;
}

struct file_operations globalfifo_drv={
    .owner   = THIS_MODULE,
    .open    = globalfifo_open,
    .release = globalfifo_release,
    .read    = globalfifo_read,
    .write   = globalfifo_write,
    .poll    = globalfifo_poll,
    .fasync  = globalfifo_fasync,
};
int globalfifo_init(void)
{
    int ret;

    //申请设备号
    globalfifo_major = register_chrdev(0,"globalfifo",&globalfifo_drv);
    globalfifo_class = class_create("globalfifo_class");
    if (IS_ERR(globalfifo_class)) {
		printk(KERN_ERR "globalfifo_class: failed to allocate class\n");
		return PTR_ERR(globalfifo_class);
	}
    device_create(globalfifo_class,NULL,MKDEV(globalfifo_major,0),NULL,"globalfifo_device");
    

    globalfifo_devp = kmalloc(sizeof(struct globalfifo_dev),GFP_KERNEL);
    if(!globalfifo_devp)
    {
        ret = -ENOMEM;
        goto fail_malloc;
    }
    memset(globalfifo_devp,0,sizeof(struct globalfifo_dev));
    //globalfifo_setup_cdev(globalfifo_devp,0);
    sema_init(&globalfifo_devp->sem,1);
    init_waitqueue_head(&globalfifo_devp->r_wait);
    init_waitqueue_head(&globalfifo_devp->w_wait);
    return 0;

    fail_malloc:device_destroy(globalfifo_class,MKDEV(globalfifo_major,0));
    class_destroy(globalfifo_class);
    unregister_chrdev(globalfifo_major,"globalfifo_chrdev");
    return ret;
}
void globalfifo_exit(void)
{
    device_destroy(globalfifo_class,MKDEV(globalfifo_major,0));
    class_destroy(globalfifo_class);
    unregister_chrdev(globalfifo_major,"globalfifo_chrdev");
    return;
}
module_init(globalfifo_init);
module_exit(globalfifo_exit);
MODULE_LICENSE("GPL"); 

 

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

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

相关文章

盘点2024年4款高效率的语音转文字工具。

语音转换文字软件真的是一种提高效率的神器&#xff0c;我在工作中常常因为手动记录太慢而选择录音。事后在形成记录&#xff0c;但效率比较低。自从知道有直接转换的工具之后&#xff0c;我有再多的录音都不怕了。如果大家也有跟我一样的工作时&#xff0c;可以试试使用这些语…

C++之STL—常用拷贝和替换算法

copy(iterator beg, iterator end, iterator dest); // 按值查找元素&#xff0c;找到返回指定位置迭代器&#xff0c;找不到返回结束迭代器位置 // beg 开始迭代器 // end 结束迭代器 // dest 目标起始迭代器 replace(iterator beg, iterator end, oldvalue, newvalue); …

儿童手抄报模板-200个(家有神兽必备)

在这个充满色彩与想象的世界里&#xff0c;每一位小朋友都是一位小小艺术家和梦想家。作为家长或老师&#xff0c;我们总是希望能为他们的学习生活增添一抹亮色&#xff0c;激发他们的创造力与探索欲。今天&#xff0c;就为大家带来一份超级实用的资源——儿童手抄报模板-200个…

Spring:强制登陆与拦截器

1.只使用session验证 &#xff08;1&#xff09;第一步&#xff1a;用户登陆时存储session ApiOperation("用户登陆") PostMapping("/login") public AppResult login(HttpServletRequest request,RequestParam("username") ApiParam("用…

将上一篇的feign接口抽取到公共api模块(包含feign接口示例)

文章目录 一、准备二、主要工作三、建立dto类四、添加多个feign接口五、测试六、目录结构6.1 父工程service-demo6.2 order-service模块6.3 product-service模块6.4 sd-api模块 一、准备 将上一篇的目录结构改造一下&#xff1a; 修改包名使根路径分别为com.hdl.order和com.h…

微信支付:chooseWXPay:fail, the permission value is offline verifying

在开发公众号微信支付的时候&#xff0c;在微信开发者工具中使用 WeixinJSBridge 唤起 微信支付&#xff0c;页面上看到微信支付的loading一闪而过&#xff0c;但是没有出现微信支付的页面。控制台log显示错误信息&#xff1a;“chooseWXPay:fail, the permission value is off…

实验1 Python语言基础一

目录 实验1 Python语言基础一1、下载安装Python&#xff0c;贴出验证安装成功截图2、建立test.py文件,运行后贴出截图&#xff0c;思考if __name”__main__”的意思和作用3、分别运行下面两种代码&#xff0c;分析运行结果产生的原因。记牢python中重要语法“tab”的作用。6、编…

企业内训|大模型/智算行业发展机会深度剖析-某数据中心厂商

北京中嘉和信通信技术有限公司于8月29日举办了一场主题为“大模型/智算行业发展机会深度剖析”的企业内训。此次培训由TsingtaoAI公司负责人汶生主讲&#xff0c;针对当前大模型技术的发展现状、应用场景及未来趋势进行了全面分析和解读。 汶生老师在培训中深入剖析了大模型的…

京东商品详情API(item_get)性能优化策略:提升数据抓取效率

在电商领域&#xff0c;快速、准确地获取商品信息对于提升用户体验、优化库存管理和市场决策至关重要。京东商品详情API&#xff08;item_get&#xff09;作为京东开放平台提供的一项重要服务&#xff0c;允许开发者获取京东平台上商品的详细信息。然而&#xff0c;如何高效利用…

xxl-job 适配达梦数据库

前言 在数字化转型的浪潮中&#xff0c;任务调度成为了后端服务不可或缺的一部分。XXL-JOB 是一个轻量级、分布式的任务调度框架&#xff0c;广泛应用于各种业务场景。达梦数据库&#xff08;DM&#xff09;&#xff0c;作为一款国内领先的数据库产品&#xff0c;已经被越来越…

详解调用钉钉AI助理消息API发送钉钉消息卡片给指定单聊用户

文章目录 前言准备工作1、在钉钉开发者后台创建一个钉钉企业内部应用&#xff1b;2、创建并保存好应用的appKey和appSecret&#xff0c;后面用于获取调用API的请求token&#xff1b;3、了解AI助理主动发送消息API&#xff1a;4、应用中配置好所需权限&#xff1a;4.1、权限点4.…

光控资本:国庆节股市能不能继续交易?A股放量大涨

早年这个时分&#xff0c;商场谈论的最多论题是&#xff0c;持股过节仍是持币过节。 而本年却大不一样&#xff0c;“国庆节股市能不能继续生意”成为这两天股民之间的梗。 今天上午&#xff0c;A股继续暴升&#xff0c;创了三个纪录。一是上午成交额为9466亿元&#xff0c;跨…

2024 maya的散布工具sppaint3d使用指南

目前工具其实可以分为三个版本 1 最老的原版 时间还是2011年的&#xff0c;只支持python2版的maya 2 作者python3更新版 后来作者看maya直到2022上还是没有类似好用方便的工具&#xff0c;于是更新到了2022版本 这个是原作者更新的2022版本&#xff0c;改成了python3&#…

一款开源的通用PDF处理神器,功能强悍!

文章目录 前言一、简介二、功能三、安装1.二进制安装2.编译安装 四、开源地址 前言 PDF是一种功能强大且广泛应用的电子文档格式&#xff0c;适用于各种文档管理和共享需求。由于PDF文档注重阅读而非编辑&#xff0c;很多文档处理会让我们非常头疼。 网上有非常多的PDF相关的…

re轻松拆分四则运算expression(^从头匹配、(?:xxxx)非捕获组、| 交替运算符联合演习)

与ai对抵聊“算式匹配”&#xff0c;发现^从头匹配、(?:xxxx)非捕获组、| “交替”运算符联合使用的妙处。 (笔记模板由python脚本于2024年09月27日 18:35:32创建&#xff0c;本篇笔记适合喜欢python喜欢正则的coder翻阅) 【学习的细节是欢悦的历程】 Python 官网&#xff1a;…

《GC8545:智能马桶电路板的卓越替代之选》

在智能马桶领域&#xff0c;电路板的性能至关重要。随着技术的不断发展&#xff0c;寻找更优质、更高效的芯片来替代传统型号已成为行业的趋势。而 GC8545 芯片以其卓越的性能和广泛的适用性&#xff0c;正逐渐成为替代其他型号的理想选择。其中最常用的就是替代的型号LV8548和…

【machine learning-13-线性回归的向量化】

向量化 向量化简洁并行计算 向量化 线性回归的向量化表示如下&#xff0c;其中w 和 x 都分别加了箭头表示这是个向量&#xff0c;后续不加也可以表示为向量&#xff0c;w和x点乘加上b&#xff0c;就构成了多元线性回归的表达方式&#xff0c;如下&#xff1a; 那么究竟为什么…

产品管理-互联网产品(2):需求管理

一、概述 需求问题解决方案&#xff0c;产品经理是解决问题专家。提出求产品经理需要考虑如下四点&#xff1a; 1、客户的目的是什么&#xff1f;2、这条需求是否隐含了解决方案&#xff1f;3、客户的潜在目标是什么&#xff1f;4、如何确定需求已得到满足&#xff1f; 二、…

SD NAND参考设计和使用提示

电路设计 参考电路&#xff1a; R1~R5 (10K-100 kΩ)是上拉电阻&#xff0c;当SD NAND处于高阻抗模式时&#xff0c;保护CMD和DAT线免受总线浮动。 即使主机使用SD NAND SD模式下的1位模式&#xff0c;主机也应通过上拉电阻上拉所有的DATO-3线。 R6&#xff08;RCLK&#…

[数据结构] 二叉树题目 (二)

目录 一. 另一颗树的子树 1.1 题目 1.2 示例 1.3 分析 1.4 解决 二. 平衡二叉树 2.1 题目 2.2 示例 2.3 分析 2.4 解决 三. 二叉树的遍历和创建 3.1 题目 3.2 示例 3.3 解决 一. 另一颗树的子树572. 另一棵树的子树 - 力扣&#xff08;LeetCode&#xff09; 1.1…