异步IO是对阻塞和轮询IO的机制补充,所谓异步IO就是在设备数据就绪时主动通知所属进程进行处理的机制。之所以说是异步是相对与被通知进程的,因为进程不知道也无法知道什么时候会被通知;这一机制非常类似于硬件上的中断。异步IO的实现也依赖于Linux内核进程的信号机制,因为异步IO就是通过SIGIO信号通知的进程,而进程在收到信号后就会像中断一样直接跳转去执行之前就注册好的信号处理接口。

用户空间  

注册信号接口函数有两个版本的接口signal(int signal,sighandler_t handler)其中signal为内核定义的信号类型目前支持30种信号,每种信号也有其缺省的处理方式如果当前进程未指定某一信号的处理方式,系统就会按系统缺省的方式处理。还有一个比较新接口他功能也更加强大是 int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact))他们的详细使用和定义内容如下:

//接口
typedef void (*sighandler_t)(int);
//原型
sighandler_t signal(int signum, sighandler_t handler));
//定义
void (*signal(int signum, void (*handler))(int)))(int);

第一个参数指定信号的值,第二个参数指定针对前面信号的处理方式或接口,可以设置为如下值:

忽略该信号(参数设为SIG_IGN)

采用系统默认方式处理信号(参数设为SIG_DFL)

可以自己实现处理方式(参数指定一个函数地址)signal()

调用成功,返回上一次为成功安装信号signum而调用signal()时的handler值,失败则返回SIG_ERR。

sigaction:

int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact));

//struct sigaction 结构体
struct sigaction {
union {
__sighandler_t _sa_handler;
void (*_sa_sigaction)(int, struct siginfo *, void *);
} _u;
sigset_t sa_mask;
unsigned long sa_flags;
void (*sa_restorer)(void);
}; //__sighandler_t
void (*__sighandler_t)(int );

sigaction的第一个参数和signal相同,第二和第三个参数都是 struct sigaction类型的指针,第一个为给这个信号新安插的处理接口,而第三个用来接收之前给这个信号安插的处理接口的返回。这个结构体中的union 是信号处理接口函数,使用的是哪一种取决与flags变量的bit标志,而sa_mask则负责在信号处理过程中对指定信号的掩蔽类似避免中断嵌套。最后一个sa_restorer现在已经废弃不建议再使用最后就是siginfo_t 结构体他是sigaction接口高级于signal接口的另一个力证。其中主要的成员是意义使用起来比较复杂在专门的地方学习,今天主要学习驱动部分的异步IO实现机制。

内核空间     

前面说了应用层的信号使用和异步IO的工作原理,接下来就是说明驱动也就是内核空间的异步IO实现机制了。在用户空间是负责接收信号那么内核空间肯定就是负责信号的释放,但是要能释放信号给指定的进程还要满足几个条件:

  1. 文件是异步方式打开的。
  2. 当前进程是文件的属主。

其中第一个条件可以同通过打开文件时指定FASYNC标志或通过fcntl(fd,F_SETFL,...)接口修改文件标志以支持异步通知。第二个则是通过使用接口fcntl(fd,F_SETOWN,getpid());进行设置。

而在内核部分的实现主要依赖一个数据结构和两个接口

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;
};
//释放信号
void kill_fasync(struct fasync_struct **, int, int);
//处理文件FASYNC 状态变更的接口
int fasync_helper(int, struct file *, int, struct fasync_struct **);

文件操作接口中有一个fasync接口函数在文件ASYNC状态变更时会被调用所以需要实现它,的实现模版大致如下:

ststic int xxx_async(int fd,struct file* filp,int mode)
{
...
...
//async 为struct fasync_struct 的指针
return fasync_helper(fd,filp,mode,&async) }

然后就是释放信号的的操作,这一部分有可能发生在中断也有可能发生的其他的接口函数中,比如写接口中可以释放IO可以读的就绪信号给进程,而读接口可以释放可以写的接口给进程等,具体的使用就是:

ststic int xxx(int fd,struct file* filp,...)
{
...
...
//async 为struct fasync_struct 的指针
kill_fasync(&async,signum,POLL_IN);
...
...
}

kill_fasync 第一个接口参数为 异步结构体指针而 第二个参数为信号值一般为SIGIO,第三个为IO事件可为如下值或如下的值的或组合
//events可以是以下几个宏的:

EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里

最后需要注意的是,在通过xxx_async接口把文件加入到异步通知列表后如果关闭文件或取消异步时需要通过这个接口把FD文件描述符设置为-1,并把async_queue设为None将文件从异步通知列表中移除。

//如
static int xxx_release(struct inode* inode,struct file *filp)
{
...
xxx_fasync(-1,filp,0);
...
return 0;
}

参考:

https://blog.csdn.net/Fury97/article/details/83041810

Linux 驱动框架---驱动中的异步的更多相关文章

  1. Linux 驱动框架---驱动中的阻塞

    描述和API 阻塞IO和非阻塞IO的应用编程时的处理机制是不同的,如果是非阻塞IO在访问资源未就绪时就直接返回-EAGAIN,反之阻塞IO则会使当前用户进程睡眠直到资源可用.从应用场景来说两种方式分别 ...

  2. Linux 驱动框架---驱动中的中断

    在单片机开发中中断就是执行过程中发生了一些事件需要及时处理,所以需要停止当前正在运行的处理的事情转而去执行中断服务函数,已完成必要的事件的处理.在Linux中断一样是如此使用但是基于常见的中断控制器的 ...

  3. Linux 驱动框架---驱动中的时间相关

    内核中的时间 Linux 系统内核对于时间的管理依赖于硬件,硬件按一定的周期产生中断,周期由内核的一个配置值HZ决定在系统启动时会将定时器配置为HZ值指定的频率产生中断:同时内核和维护一个64位(X8 ...

  4. Linux 驱动框架---驱动中的并发

    并发指多个执行单元被同时.并行的执行,而并发执行的单元对共享资源的访问就容易导致竟态.并发产生的情况分为抢占和并行(多核)和硬抢占(中断).Linux为解决这一问题增加了一系列的接口来解决并发导致的竟 ...

  5. 驱动框架入门——以LED为例[【转】

    本文转载自;http://blog.csdn.net/oqqHuTu12345678/article/details/72783903 以下内容源于朱有鹏<物联网大讲堂>课程的学习,如有侵 ...

  6. I2C驱动框架(四)

    参考:I2C子系统之platform_driver初始化——I2C_adap_s3c_init() 在完成platform_device的添加之后,i2c子系统将进行platform_driver的注 ...

  7. Linux驱动框架之framebuffer驱动框架

    1.什么是framebuffer? (1)framebuffer帧缓冲(一屏幕数据)(简称fb)是linux内核中虚拟出的一个设备,framebuffer向应用层提供一个统一标准接口的显示设备.帧缓冲 ...

  8. linux驱动基础系列--linux spi驱动框架分析

    前言 主要是想对Linux 下spi驱动框架有一个整体的把控,因此会忽略某些细节,同时里面涉及到的一些驱动基础,比如平台驱动.设备模型等也不进行详细说明原理.如果有任何错误地方,请指出,谢谢! spi ...

  9. Linux下USB驱动框架分析【转】

    转自:http://blog.csdn.net/brucexu1978/article/details/17583407 版权声明:本文为博主原创文章,未经博主允许不得转载. http://www.c ...

随机推荐

  1. RPC 实战与原理 精简版

    什么是 RPC? RPC 有什么作用? RPC 步骤 为什么需要序列化? 零拷贝 什么是零拷贝? 为什么需要零拷贝? 如何实现零拷贝? Netty 的零拷贝有何不同? 动态代理实现 HTTP/2 特性 ...

  2. 订阅者模式,公众号、B站、快手用了都说好!

    大家好,今天和大家来聊一个新的设计模式--订阅者模式. 这个模式在我们的生活当中非常常见,可以说是几乎所有的媒体平台都用或多或少地用到了这个模式.比如公众号,我们来仔细梳理一下公众号这个平台当中的整个 ...

  3. 参数模型检验过滤器 .NetCore版

    最近学习 .NETCore3.1,发现过滤器的命名空间有变化. 除此以外一些方法的名称和使用方式也有变动,正好重写一下. 过滤器的命名空间的变化 原先:System.Web.Http.Filters; ...

  4. Netty编解码器(理论部分)

    背景知识 在了解Netty编解码之前,先回顾一下JAVA的编解码: 编码(Encode):在java中称之为序列化,把内存中易丢失的数据结构或对象状态转换成另一种可存储(存储到磁盘),可在网络间传输的 ...

  5. Oracle数据库查询锁表及解锁

    一.查询哪些表被锁以及查看锁表得会话及操作系统进程ID 其中locked_mode为锁的级别,spid为数据库所在操作系统的进程id select c.sid, c.serial#, c.userna ...

  6. XCTF-phoenix100

    前期工作 查壳无壳,界面是普通的输入flag点击验证 逆向分析 文件结构只有一个MainActively,查看MainActively代码 public class MainActivity exte ...

  7. Javascript关键字,条件语句,函数及函数相关知识

    关键字 条件语句 作用域 回调 关键字 根据规定,关键字是保留的,不能用作变量名或函数名. 下面是一些ECMAScript关键字的完整列表. break ,case,catch,continue,de ...

  8. Centos 搭建Hadoop

    Centos搭建Hadoop 一.搭建Hadoop需要JDK环境,首先配置JDK 二.下载haoop 三.在Centos服务器上解压下载好的安装包 四.修改配置文件 4.1 hadoop-env.sh ...

  9. sql语句定义和执行顺序

    sql语句定义的顺序 (1) SELECT (2)DISTINCT<select_list> (3) FROM <left_table> (4) <join_type&g ...

  10. D - LOL UVALive - 8521 (状压dp)

    https://nanti.jisuanke.com/t/A1616 思路:dp[i][j]表示前i列里面选了情况j有多少种组合方案 #include<bits/stdc++.h> usi ...