关闭 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);
    上一篇返回首页 下一篇

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

    别人在看

    正版 Windows 11产品密钥怎么查找/查看?

    还有3个月,微软将停止 Windows 10 的更新

    Windows 10 终止支持后,企业为何要立即升级?

    Windows 10 将于 2025年10 月终止技术支持,建议迁移到 Windows 11

    Windows 12 发布推迟,微软正全力筹备Windows 11 25H2更新

    Linux 退出 mail的命令是什么

    Linux 提醒 No space left on device,但我的空间看起来还有不少空余呢

    hiberfil.sys文件可以删除吗?了解该文件并手把手教你删除C盘的hiberfil.sys文件

    Window 10和 Windows 11哪个好?答案是:看你自己的需求

    盗版软件成公司里的“隐形炸弹”?老板们的“法务噩梦” 有救了!

    IT头条

    公安部:我国在售汽车搭载的“智驾”系统都不具备“自动驾驶”功能

    02:03

    液冷服务器概念股走强,博汇、润泽等液冷概念股票大涨

    01:17

    亚太地区的 AI 驱动型医疗保健:2025 年及以后的下一步是什么?

    16:30

    智能手机市场风云:iPhone领跑销量榜,华为缺席引争议

    15:43

    大数据算法和“老师傅”经验叠加 智慧化收储粮食尽显“科技范”

    15:17

    技术热点

    商业智能成CIO优先关注点 技术落地方显成效(1)

    用linux安装MySQL时产生问题破解

    JAVA中关于Map的九大问题

    windows 7旗舰版无法使用远程登录如何开启telnet服务

    Android View 事件分发机制详解

    MySQL用户变量的用法

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

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