关闭 x
IT技术网
    技 采 号
    ITJS.cn - 技术改变世界
    • 实用工具
    • 菜鸟教程
    IT采购网 中国存储网 科技号 CIO智库

    IT技术网

    IT采购网
    • 首页
    • 行业资讯
    • 系统运维
      • 操作系统
        • Windows
        • Linux
        • Mac OS
      • 数据库
        • MySQL
        • Oracle
        • SQL Server
      • 网站建设
    • 人工智能
    • 半导体芯片
    • 笔记本电脑
    • 智能手机
    • 智能汽车
    • 编程语言
    IT技术网 - ITJS.CN
    首页 » HTML5 »Linux设备驱动之异步通知和异步I/O

    Linux设备驱动之异步通知和异步I/O

    2015-04-29 00:00:00 出处:时光漫步LH
    分享

    在设备驱动中使用异步通知可以使得对设备的访问可进行时,由驱动主动通知应用程序进行访问。因此,使用无阻塞I/O的应用程序无需轮询设备是否可访问,而阻塞访问也可以被类似“中断”的异步通知所取代。异步通知类似于硬件上的“中断”概念,比较准确的称谓是“信号驱动的异步I/O”。

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

    影响:阻塞–应用程序无需轮询设备是否可以访问

    非阻塞–中断进行通知

    即:由驱动发起,主动通知应用程序

    2、linux异步通知编程

    2.1 linux信号

    作用:linux系统中,异步通知使用信号来实现

    函数原型为:

    void (*signal(int signum,void (*handler))(int)))(int)

    原型比较难理解可以分解为

    typedef void(*sighandler_t)(int);
    
    sighandler_t signal(int signum,sighandler_t handler);

    第一个参数是指定信号的值,第二个参数是指定针对前面信号的处理函数

    2.2 信号的处理函数(在应用程序端捕获信号)

    signal()函数

    例子:

    //启动信号机制
    
    void sigterm_handler(int sigo)
    {
    
    char data[MAX_LEN];
    int len;
    len = read(STDIN_FILENO,&data,MAX_LEN);
    data[len] = 0;
    printf("Input available:%sn",data);
    exit(0);
    
    }
    
    int main(void)
    {
    
    int oflags;
    //启动信号驱动机制
    
    signal(SIGIO,sigterm_handler);
    fcntl(STDIN_FILENO,F_SETOWN,getpid());
    oflags = fcntl(STDIN_FILENO,F_GETFL);
    fctcl(STDIN_FILENO,F_SETFL,oflags | FASYNC);
    //建立一个死循环,防止程序结束
    
    whlie(1);
    
    return 0;
    
    }

    2.3 信号的释放 (在设备驱动端释放信号)

    为了是设备支持异步通知机制,驱动程序中涉及以下3项工作

    (1)、支持F_SETOWN命令,能在这个控制命令处理中设置filp->f_owner为对应的进程ID。不过此项工作已由内核完成,设备驱动无须处理。

    (2)、支持F_SETFL命令处理,每当FASYNC标志改变时,驱动函数中的fasync()函数得以执行。因此,驱动中应该实现fasync()函数

    (3)、在设备资源中可获得,调用kill_fasync()函数激发相应的信号

    设备驱动中异步通知编程:

    (1)、fasync_struct加入设备结构体模板中

    (2)、两个函数

    处理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);

    和其他结构体指针放到设备结构体中,模板如下

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

    2.4 在设备驱动中的fasync()函数中,只需简单地将该函数的3个参数以及fasync_struct结构体指针的指针作为第四个参数传入fasync_helper()函数就可以了,模板如下

    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);
    }

    2.5 在设备资源可获得时应该调用kill_fasync()函数释放SIGIO信号,可读时第三个参数为POLL_IN,可写时第三个参数为POLL_OUT,模板如下

    static ssize_t xxx_write(struct file *filp,const char __user *buf,size_t count,loff_t *ppos)
    
    {
    
    struct xxx_dev *dev = filp->private_data;
    ...
    
    if(dev->async_queue)
    
    kill_fasync(&dev->async_queue,GIGIO,POLL_IN);
    
    ...
    
    }

    2.6 最后在文件关闭时,要将文件从异步通知列表中删除

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

    3、linux2.6异步I/O

    同步I/O:linux系统中最常用的输入输出(I/O)模型是同步I/O,在这个模型中,当请求发出后,应用程序就会阻塞,知道请求满足

    异步I/O:I/O请求可能需要与其它进程产生交叠

    Linux 系统中最常用的输入/输出(I/O)模型是同步 I/O。在这个模型中,当请求发出之后,应用程序就会阻塞,直到请求满足为止。这是很好的一种解决方案,因为调用应用程序在等待 I/O 请求完成时不需要使用任何中央处理单元(CPU)。但是在某

    些情况下,I/O 请求可能需要与其他进程产生交叠。可移植操作系统接口(POSIX)异步 I/O(AIO)应用程序接口(API)就提供了这种功能

    4.1、AIO系列API:

    aio_read–异步读

    aio_read 函数的原型如下:

    int aio_read( struct aiocb *aiocbp );

    aio_read()函数在请求进行排队之后会立即返回。假如执行成功,返回值就为 0;假如出现错误,返回值就为 1,并设置 errno 的值。

    aio_write–异步写

    aio_write()函数用来请求一个异步写操作,其函数原型如下:

    int aio_write( struct aiocb *aiocbp );

    aio_write()函数会立即返回,说明请求已经进行排队(成功时返回值为 0,失败时返回值为 1,并相应地设置 errno。

    aio_error–确定请求的状态

    aio_error 函数被用来确定请求的状态,其原型如下:

    int aio_error( struct aiocb *aiocbp );

    这个函数可以返回以下内容。

    EINPROGRESS:说明请求尚未完成。

    ECANCELLED:说明请求被应用程序取消了。

    -1:说明发生了错误,具体错误原因由 errno 记录。

    aio_return–获得异步操作的返回值

    异步 I/O 和标准块 I/O 之间的另外一个区别是不能立即访问这个函数的返回状态,因为并没有阻塞在 read()调用上。在标准的 read()调用中,返回状态是在该函数返回时提供的。但是在异步 I/O 中,大家要使用 aio_return()函数。这个函数的原型如下:

    ssize_t aio_return( struct aiocb *aiocbp );

    只有在 aio_error()调用确定请求已经完成(可能成功,也可能发生了错误)之后,才会调用这个函数。aio_return()的返回值就等价于同步情况中 read 或 write 系统调用的返回值(所传输的字节数,假如发生错误,返回值就为 1)。

    aio_suspend–挂起异步操作,知道异步请求完成为止

    aio_suspend()函数来挂起(或阻塞)调用进程,直到异步请求完成为止,此时会产生一个信号,或者发生其他超时操作。调用者提供了一个 aiocb 引用列表,其中任何一个完成都会导致 aio_suspend()返回。aio_suspend 的函数原型如下:

    int aio_suspend( const struct aiocb *const cblist[], int n, const struct timespec *timeout );

    aio_cancel–取消异步请求

    aio_cancel()函数允许用户取消对某个文件描述符执行的一个或所有 I/O 请求。其原型如下:

    int aio_cancel( int fd, struct aiocb *aiocbp );

    假如要取消一个请求,用户需提供文件描述符和 aiocb 引用。假如这个请求被成功取消了,那么这个函数就会返回 AIO_CANCELED。假如请求完成了,这个函数就会返回AIO_NOTCANCELED。 假如要取消对某个给定文件描述符的所有请求,用户需要提供这个文件的描述符以及一个对 aiocbp 的 NULL 引用。假如所有的请求都取消了,这个函数就会返回AIO_CANCELED ;假如至少有一个请求没有被取消,那么这个函数就会返回AIO_NOT_CANCELED;假如没有一个请求可以被取消,那么这个函数就会返回AIO_ALLDONE。然后,可以使用 aio_error()来验证每个 AIO 请求,假如某请求已经被取消了,那么 aio_error()就会返回 1,并且 errno 会被设置为 ECANCELED。

    lio_listio–同时发起多个传输(一次系统调用可以启动大量的I/O操作)

    lio_listio()函数可用于同时发起多个传输。这个函数非常重要,它使得用户可以在一个系统调用(一次内核上下文切换)中启动大量的 I/O 操作。lio_listio API 函数的原型如下:

    int lio_listio( int mode, struct aiocb *list[], int nent, struct sigevent *sig );

    mode 参数可以是 LIO_WAIT 或 LIO_NOWAIT。LIO_WAIT 会阻塞这个调用,直到所有的 I/O 都完成为止。在操作进行排队之后,LIO_NOWAIT 就会返回。list 是一个 aiocb 引用的列表,最大元素的个数是由 nent 定义的。假如 list 的元素为 NULL,lio_listio()会将其忽略。

    3.2、使用信号作为AIO的通知

    信号作为异步通知的机制在AIO中依然使用,为了使用信号,使用AIO的应用程序同样需要定义信号处理程序,在指定的信号被触发时,调用这个处理程序,作为信号上下文的一部分,特定的 aiocb 请求被提供给信号处理函数用来区分 AIO 请求。 下面代码清单给出了使用信号作为 AIO 异步 I/O 通知机制的例子。

    1 /*设置异步 I/O 请求*/
    2 void setup_io(...) 
    3 { 
    4 int fd; 
    5 struct sigaction sig_act; 
    6 struct aiocb my_aiocb; 
    7 ... 
    8 /* 设置信号处理函数 */
    9 sigemptyset(&sig_act.sa_mask); 
    10 sig_act.sa_flags = SA_SIGINFO; 
    11 sig_act.sa_sigaction = aio_completion_handler; 
    12 
    13 /* 设置 AIO 请求 */
    14 bzero((char*) &my_aiocb, sizeof(struct aiocb)); 
    15 my_aiocb.aio_fildes = fd; 
    16 my_aiocb.aio_buf = malloc(BUF_SIZE + 1); 
    17 my_aiocb.aio_nbytes = BUF_SIZE; 
    18 my_aiocb.aio_offset = next_offset; 
    19 
    20 /* 连接 AIO 请求和信号处理函数 */
    21 my_aiocb.aio_sigevent.sigev_notify = SIGEV_SIGNAL; 
    22 my_aiocb.aio_sigevent.sigev_signo = SIGIO; 
    23 my_aiocb.aio_sigevent.sigev_value.sival_ptr = &my_aiocb; 
    24 
    25 /* 将信号与信号处理函数绑定 */
    26 ret = sigaction(SIGIO, &sig_act, NULL); 
    27 ... 
    28 ret = aio_read(&my_aiocb); /*发出异步读请求*/
    29 } 
    30 
    31 /*信号处理函数*/
    32 void aio_completion_handler(int signo, siginfo_t *info, void *context) 
    33 { 
    34 struct aiocb *req; 
    35 
    36 /* 确定是我们需要的信号*/
    37 if (info->si_signo == SIGIO) 
    38 { 
    39 req = (struct aiocb*)info->si_value.sival_ptr; /*获得 aiocb*/
    40 
    41 /* 请求的操作完成了吗  */
    42 if (aio_error(req) == 0) 
    43 { 
    44 /* 请求的操作完成,获取返回值 */
    45 ret = aio_return(req); 
    46 } 
    47 } 
    48 return ; 
    49 }

    3.3 使用回调函数作为AIO的通知

    代码清单给出了使用回调函数作为 AIO 异步 I/O 请求完成的通知机制的例子

    1 /*设置异步 I/O 请求*/
    2 void setup_io(...)
    3 {
    4 int fd;
    5 struct aiocb my_aiocb;
    6 ...
    7 /* 设置 AIO 请求 */
    8 bzero((char*) &my_aiocb, sizeof(struct aiocb));
    9 my_aiocb.aio_fildes = fd;
    10 my_aiocb.aio_buf = malloc(BUF_SIZE + 1);
    11 my_aiocb.aio_nbytes = BUF_SIZE;
    12 my_aiocb.aio_offset = next_offset;
    13 
    14 /* 连接 AIO 请求和线程回调函数 */
    15 my_aiocb.aio_sigevent.sigev_notify = SIGEV_THREAD;
    16 my_aiocb.aio_sigevent.notify_function = aio_completion_handler;
    17 /*设置回调函数*/
    18 my_aiocb.aio_sigevent.notify_attributes = NULL;
    19 my_aiocb.aio_sigevent.sigev_value.sival_ptr = &my_aiocb;
    20 ... ret = aio_read(&my_aiocb); //发起 AIO 请求
    21 }
    22 
    23 /* 异步 I/O 完成回调函数 */
    24 void aio_completion_handler(sigval_t sigval)
    25 {
    26 struct aiocb *req;
    27 req = (struct aiocb*)sigval.sival_ptr;
    28 
    29 /* AIO 请求完成  */
    30 if (aio_error(req) == 0)
    31 {
    32 /* 请求完成,获得返回值 */
    33 ret = aio_return(req);
    34 }
    35 
    36 return ;
    37 }

    3.4 AIO与设备驱动

    在内核中,每个I/O请求都对应一个kiocb结构体,其ki_filp成员只想对应的file指针,通过is_sync_kiocb判断某kiocb是否为同步I/O请求,假如是返回真,表示为异步I/O请求。

    块设备和网络设备:本身是异步的

    字符设备:必须明确应支持AIO(极少数是异步I/O操作)

    字符设备驱动程序中file_operations 包含 3 个与 AIO 相关的成员函数,如下所示:

    ssize_t (*aio_read) (struct kiocb *iocb, char *buffer, size_t count, loff_t offset);
    
    ssize_t (*aio_write) (struct kiocb *iocb, const char *buffer, size_t count, loff_t offset);
    
    int (*aio_fsync) (struct kiocb *iocb, int datasync);
    上一篇返回首页 下一篇

    声明: 此文观点不代表本站立场;转载务必保留本文链接;版权疑问请联系我们。

    别人在看

    电脑屏幕不小心竖起来了?别慌,快捷键搞定

    Destoon 模板存放规则及语法参考

    Destoon系统常量与变量

    Destoon系统目录文件结构说明

    Destoon 系统安装指南

    Destoon会员公司主页模板风格添加方法

    Destoon 二次开发入门

    Microsoft 将于 2026 年 10 月终止对 Windows 11 SE 的支持

    Windows 11 存储感知如何设置?了解Windows 11 存储感知开启的好处

    Windows 11 24H2 更新灾难:系统升级了,SSD固态盘不见了...

    IT头条

    Synology 更新 ActiveProtect Manager 1.1 以增强企业网络弹性和合规性

    00:43

    新的 Rubrik Agent Cloud 加速了可信的企业 AI 代理部署

    00:34

    宇树科技 G1人形机器人,拉动一辆重达1.4吨的汽车

    00:21

    Cloudera 调查发现,96% 的企业已将 AI 集成到核心业务流程中,这表明 AI 已从竞争优势转变为强制性实践

    02:05

    投资者反对马斯克 1 万亿美元薪酬方案,要求重组特斯拉董事会

    01:18

    技术热点

    大型网站的 HTTPS 实践(三):基于协议和配置的优化

    ubuntu下右键菜单添加新建word、excel文档等快捷方式

    Sublime Text 简明教程

    用户定义SQL Server函数的描述

    怎么在windows 7开始菜单中添加下载选项?

    SQL Server 2016将有哪些功能改进?

      友情链接:
    • IT采购网
    • 科技号
    • 中国存储网
    • 存储网
    • 半导体联盟
    • 医疗软件网
    • 软件中国
    • ITbrand
    • 采购中国
    • CIO智库
    • 考研题库
    • 法务网
    • AI工具网
    • 电子芯片网
    • 安全库
    • 隐私保护
    • 版权申明
    • 联系我们
    IT技术网 版权所有 © 2020-2025,京ICP备14047533号-20,Power by OK设计网

    在上方输入关键词后,回车键 开始搜索。Esc键 取消该搜索窗口。