Linux驱动中的fasync(异步通知)和fsync

news2024/11/16 22:25:45

一、fsync

用来同步设备的写入操作,考虑把一块设局写入到硬盘的操作,如果使用write函数,函数返回后只能保证数据被写入到驱动程序或者内核管理的数据缓存中,而无法保证数据被真正写入到硬盘的存储块里。但是fync可以做到这一点,函数在数据块没有真正写道硬盘的存储卡里时不会返回,若返回则意味着要么设备在写入过程发生错误,要么数据已经写入到了设备的存储块。

显然大量的工作都在驱动程序这边,驱动程序需要针对不同的设备实现wirte和fsync例程,以满足应用程序的需求。

对于字符设备而言,大部分驱动程序都没有实现这个例程,只是简单将它们的struct file_operations对象的fsync指针赋值为NULL;对于块设备而言,则需要实现fsync。

    int (*fsync) (struct file *, loff_t, loff_t, int datasync);

二、fasync(异步通知)

在异步非阻塞型I/O(AIO)中,基于驱动程序poll例程之上的应用层的三个函数poll、select、epoll,它们在与驱动程序沟通数据是否就绪时,本质是采用了轮询的方式:应用程序在一组由设备文件描述符的集合上调用poll,由此获得设备可否进行无阻塞操作的信息。

其实除了轮询的方式,应用程序与设备驱动的沟通还有一种类似中断的方式;当设备中的数据就绪时,作为通知的方式,驱动程序给应用程序发送一个signal。

异步通知使用了系统调用的signal()和sigcation()函数。signal()函数会让一个信号和一个函数对应,每当接收到这个信号时就会调用相应的函数来处理。异步通知处理过程中用户空间和设备驱动的交互如下:

应用层的处理

1)设置信号处理函数:

我们使用中断的时候需要设置中断处理函数,同样的,如果要在应用程序中使用信号,那么就必须设置信号所使用的信号处理函数。在应用程序中使用 signal 函数来设置指定信号的处理函数:

//signum:要设置处理函数的信号。
//handler: 信号的处理函数。
//返回值: 设置成功的话返回信号的前一个处理函数,设置失败的话返回 SIG_ERR。
sighandler_t signal(int signum, sighandler_t handler);

2)fcntl函数需要做二件事:

将本应用程序的进程号告诉给内核使用:

/*通过fcntl函数的F_SETOWN命令将进程的ID告诉驱动程序,这样当驱动发现设备的数据就绪时才
直到要通知哪个进程*/
fcntl(fd, F_SETOWN, getpid());

使用如下两行程序开启异步通知:

/*获取当前的进程状态*/
flags = fcntl(fd, F_GETFL);
/*
通过F_SETFL命令设置FASYNC标志让驱动程序启动异步通知机制(开启当前进程异步通知功能),
经过这一步,驱动程序中的 fasync 函数就会执行。
*/                     
fcntl(fd, F_SETFL, flags | FASYNC);        

下面是内核的系统调用接口,从中可以看到会调用驱动程序提供的fasync例程:

static int setfl(int fd, struct file * filp, unsigned long arg)
{
    ...
    if (((arg ^ filp->f_flags) & FASYNC) && filp->f_op->fasync) {
        error = filp->f_op->fasync(fd, filp, (arg & FASYNC) != 0);
         ...
    }
    ...
}

static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
        struct file *filp)
{
    long err = -EINVAL;
    ...
    switch (cmd) {
    ...
    case F_SETFL:
        /*会在内部直接调用驱动程序提供的fasync例程*/
        err = setfl(fd, filp, arg);
        break;
    ...
    case F_SETOWN:
        /*将要通知进程的ID相关信息记录在filp->f_owner中,驱动程序无需对此做出回应*/
        err = f_setown(filp, arg, 1);
        break;
     ...
    default:
        break;
    }
    return err;
}

内核层的处理

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

设备驱动中异步通知编程比较简单, 主要用到一项数据结构和两个函数。使用时,首先需要定义一个struct fasync_struct类型的指针,当用户程序调用fcntl用F_SETFL命令来设置或者清除FASYNC标志时,驱动程序应该在其fasync例程中调用内核提供的fasync_helper函数在struct fasync_struct指针所指向的链表中增加或者删除一个节点,每个节点代表一个需要通知的进程。其次,当进程所需的数据就绪或者关注的某个事件发生时,驱动程序使用内核提供的kill_fasync函数负责向维护的struct fasync_struct链表中的每个进程发送通知信号。

数据结构是fasync_struct结构体:

支持异步通知的设备结构体模板如下:

struct fasync_struct {
    rwlock_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;
};

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

两个函数分别是:

1)需要在设备驱动中实现 file_operations 操作集中的 fasync 函数:

    int (*fasync) (int, struct file *, int);

该函数一般通过调用 fasync_helper 函数来初始化前面定义的 fasync_struct结构体指针。fasync_helper主要将当前要通知的进程加入一个链表或者从链表中移除,这取决于应用程序调用fcntl时是否设置了FASYNC标志。

/*
1)第二个参数on:上面提到的setfl()函数中,传递给它的是一个条件表达式(arg & FASYNC) != 0,
这意味着如果应用程序在调用fcntl时,对于F_SETFL命令使用的参数arg设置了FASYNC,那么
(arg & FASYNC) != 0结果为1,所以参数on将为1,这表明应用程序正在启用驱动程序的异步通知
机制;反之,当对fcntl函数使用F_SETFL命令时清除了FASYNC标志,将导致驱动程序的fasync例程
关闭异步通知特性。
2)所以fasync_helper的主要作用是维护一个需要通知的进程链表fapp,如果应用程序需要获得异步通知
的能力,那么需要通过fcntl的F_SETFL命令设置FASYNC标志,如果设置了该标志,驱动程序的fasync例程
在调用fasync_helper时将用fasync_add_entry将需要通知的进程加入到驱动程序维护的一个链表中,
否则使用fasync_remove_entry将其从链表中移除。
3)链表的类型为struct fasync_struct,链表中的每个节点对象代表着一个需要通知的进程,进程ID
信息存放再节点对象的fa_file->f_owner中。例如当有二个用户进程需要得到设备驱动程序的异步通知时,
内核在实现fasync_helper函数时,对于一个新加入的struct fasync_struct对象节点,会将其放到
链表的头部,这就意味着应用层后调用fcntl(fd, F_SETFL, FASYNC)的应用程序反而先得到通知。
*/
int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)
{
    if (!on)
        return fasync_remove_entry(filp, fapp);
    return fasync_add_entry(fd, filp, fapp);
}

使用模板:

在设备驱动的fasync函数中, 只需要简单地将该函数的3个参数以及fasync_struct结构体指针的指针作为第4个参数传入fasync_helper函数即可。

//在关闭驱动文件的时候需要在 file_operations 操作集中的 release 函数中释放fasync_struct

2)kill_fasync则在设备中的某一事件发生时负责通知链表中的所有相关的进程。

现在,驱动程序已经将需要通知的进程所在的节点加入了fasync链表。当需要的条件满足时,比如进程所请求的数据已经就绪,驱动程序需要使用kill_fasync来向fasync链表中的每个等待通知的进程发送通知信号。

/*通过while循环遍历fasync链表,定义每个进程调用send_sigio来向其发送信号SIGIO以通知进程*/
static void kill_fasync_rcu(struct fasync_struct *fa, int sig, int band)
{
    while (fa) {
        struct fown_struct *fown;
        unsigned long flags;

        if (fa->magic != FASYNC_MAGIC) {
            printk(KERN_ERR "kill_fasync: bad magic number in "
                   "fasync_struct!\n");
            return;
        }
        read_lock_irqsave(&fa->fa_lock, flags);
        if (fa->fa_file) {
            fown = &fa->fa_file->f_owner;
            /* Don't send SIGURG to processes which have not set a
               queued signum: SIGURG has its own default signalling
               mechanism. */
            if (!(sig == SIGURG && fown->signum == 0))
                send_sigio(fown, fa->fa_fd, band);
        }
        read_unlock_irqrestore(&fa->fa_lock, flags);
        fa = rcu_dereference(fa->fa_next);
    }
}

void kill_fasync(struct fasync_struct **fp, int sig, int band)
{
    if (*fp) {
        rcu_read_lock();
        kill_fasync_rcu(rcu_dereference(*fp), sig, band);
        rcu_read_unlock();
    }
}

使用模板:

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

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

相关文章

查找、排序、二叉树的算法,统统记录于此。

文章目录一、查找1. 无序表的顺序查找2. 折半查找3. 分块查找4. 二叉排序树BST5. 哈希表查找二、排序1. 不带哨兵的直接插入排序2. 带哨兵的直接插入排序3. 带哨兵、折半查找的直接插入排序4. 希尔排序5. 冒泡排序6. 快速排序7. 选择排序8. 堆排序9. 归并排序二叉树1. 递归先序…

八,iperf3源代码分析:状态机及状态转换过程--->运行正向TCP单向测试时的客户端代码

本文目录一、测试用命令二、iperf3客户端状态机中各个状态解析状态机迁移图运行正向TCP单向测试时的客户端的状态列表三、iperf3客户端状态机迁移分析A-初始化测试对象(NA--->初始化状态):B-建立控制连接,等待服务端PARAM_EXCHANGE的指令&…

西电机试数据结构核心算法与习题代码汇总(机考真题+核心算法)

文章目录前言一、链表问题1.1 反转链表1.1.1 题目1.1.2 代码1.2 多项式加减法1.2.1 题目1.2.2 代码二、队列和栈2.1 学生退学2.1.1 问题2.1.2 代码三、矩阵和串题目3.1 矩阵对角线求和3.1.1 问题3.1.2 代码四、排序问题4.1 多元素排序4.1.1 问题4.1.2 代码五、二叉树5.1 相同二…

synchronize优化偏向锁

偏向锁 轻量级锁在没有竞争时(只有自己一个线程),仍然会尝试CAS替换mark word; 会造成一定的性能的损耗; JDK6之中引入了偏向锁进行优化,第一次使用时线程ID注入到Mark word中,之后重入不再进…

旅游预约APP开发具有什么优势和功能

旅游活动目前正在作为广大用户休闲娱乐的一个首选内容,不仅是公司团建活动可以选择旅游,而且一些节假日也可以集结自己的亲朋好友来一次快乐有趣的旅游活动,随着当代人对于旅游的需求呈现上升的趋势,也让旅游预约APP开发开始流行并…

大家都在用哪些研发流程管理软件?

全球知名的10款流程管理软件分享:1.IT/研发项目流程管理:PingCode;2.通用项目流程管理:Worktile;3.销售流程管理:Salesforce Workflow;4.合同流程管理:Agiloft;5.IBM Bus…

20230308 APDL Lsdyna结构学习笔记

可以用鼠标右键进行结构的旋转视图。 一、编辑材料 输入参数分别为: 密度; 弹性模量; 泊松比; 屈服应力; 切线模量 由于模型是分块建立的,这里需要把模型进行粘接 点击booleans(布尔工具) 点击Glue、areas,结构物是由面单元构成的

ReactDOM.render函数内部做了啥

ReactDOM.render函数是整个 React 应用程序首次渲染的入口函数&#xff0c;它的参数是什么&#xff0c;返回值是什么&#xff0c;函数内部做了什么&#xff1f; ReactDOM.render(<App />, document.getElementById("root")); 前序 首先看下首次渲染时候&…

二叉树OJ题目详解

根据二叉树创建字符串 采用前序遍历的方式&#xff0c;将二叉树转换成一个由括号和数字组成的字符串。 再访问每一个节点时&#xff0c;需要分情况讨论。 如果这个节点的左子树不为空&#xff0c;那么字符串应加上括号和左子树的内容&#xff0c;然后判断右子树是否为空&#x…

VBA小模板,跨表统计的2种写法

目标 1 统计一个excel 文件里&#xff0c;多个sheet里的内容2 有的统计需求是&#xff0c;每个表只单表统计&#xff0c;只是进行批量操作3 有的需求是&#xff0c;多个表得某些行列累加等造出来得文件 2 实现方法1 &#xff08;可能只适合VBAEXCEL&#xff0c;不太干净的写法…

一文带你了解,前端模块化那些事儿

文章目录前端模块化省流&#xff1a;chatGPT 总结一、参考资料二、发展历史1.无模块化引出的问题:横向拓展2.IIFE3.Commonjs(cjs)4.AMD引出的问题&#xff1a;5.CMD6.UMD7.ESM往期精彩文章前端模块化 省流&#xff1a;chatGPT 总结 该文章主要讲述了前端模块化的发展历史和各个…

css伪类和伪元素的区别

文章目录什么是css伪类和伪元素css伪类和伪元素有什么用&#xff1f;css伪类的具体使用常见的伪类伪元素的具体使用常见的伪元素什么是css伪类和伪元素 伪类和为元素是两个完全不同且重要的概念&#xff0c;它们的作用是给元素添加一些特殊的效果或样式 伪类用于选择某个元素的…

Kalman Filter in SLAM (6) ——Error-state Kalman Filter (EsKF, 误差状态卡尔曼滤波)

文章目录0.前言1. IMU的误差状态空间方程2. 误差状态观测方程3. 误差状态卡尔曼滤波4. 误差状态卡尔曼滤波方程细节问题0.前言 这里先说一句&#xff1a;什么误差状态卡尔曼&#xff1f;完全就是在扯淡&#xff01; 回想上面我们推导的IMU的误差状态空间方程&#xff0c;其实…

乐山持点科技:抖客推广准入及准出管理规则

抖音小店平台新增《抖客推广准入及准出管理规则》&#xff0c;本次抖音规则具体如下&#xff1a;第一章 概述1.1 目的及依据为维护精选联盟平台经营秩序&#xff0c;保障精选联盟抖客、商家、消费者等各方的合法权益;根据《巨量百应平台服务协议》、《“精选联盟”服务协议(推广…

【GNN/深度学习】常用的图数据集(资源包)

【GNN/深度学习】常用的图数据集&#xff08;图结构&#xff09; 文章目录【GNN/深度学习】常用的图数据集&#xff08;图结构&#xff09;1. 介绍2. 图数据集2.1 Cora2.2 Citeseer2.3 Pubmed2.4 DBLP2.5 ACM2.6 AMAP & AMAC2.7 WIKI2.8 COCS2.9 BAT2.10 EAT2.11 UAT2.12 C…

第十三届蓝桥杯省赛Python大学B组复盘

目录 一、试题B&#xff1a;寻找整数 1、题目描述 2、我的想法 3、官方题解 4、另解 二、试题E&#xff1a;蜂巢 1、题目描述 2、我的想法 3、官方题解 三、试题F&#xff1a;消除游戏 1、题目描述 2、我的想法&#xff08;AC掉58.3%&#xff0c;剩下全超时&#x…

Substrate 基础教程(Tutorials) -- 监控节点指标

Substrate 公开有关网络操作的度量。例如&#xff0c;您可以收集有关您的节点连接了多少个对等节点、您的节点使用了多少内存以及正在生成的块数量的信息。为了捕获和可视化Substrate节点公开的度量&#xff0c;您可以配置和使用Prometheus和Grafana等工具。本教程演示如何使用…

C++学习笔记(以供复习查阅)

视频链接 代码讲义 提取密码: 62bb 文章目录1、C基础1.1 C初识&#xff08;1&#xff09; 第一个C程序&#xff08;2&#xff09;注释&#xff08;3&#xff09;变量&#xff08;4&#xff09;常量&#xff08;5&#xff09;关键字&#xff08;6&#xff09;标识符命名规则1.2 …

mysql 导入超大sql文件

mysql -u root -p 登录mysql命令 可以登陆mysql服务器使用source命令导入&#xff0c;会快很多&#xff0c;我这里导入500M&#xff0c;大概用了5分钟。 1. liunx登陆mysql mysql -u 用户名 -p 数据库名 然后输入密码 登陆mysql控制台后&#xff0c;执行source命令&#xf…

Maven - Linux 服务器 Maven 环境安装与测试

目录 一.引言 二.安装流程 1.获取安装包 2.解压并安装 3.配置环境 4.mvn 验证 三.测试踩坑 1.Permission denied 2.Plugin or dependencies Error 一.引言 通道机上的 java 项目需要 mvn package 提示没有 mvn 命令&#xff0c;下面记录下安装 maven 的全过程。 二.安…