我是靠谱客的博主 英勇月光,最近开发中收集的这篇文章主要介绍linux驱动笔记(五):异步通知,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

一、应用程序对异步通知的处理包括以下三步:

1、注册信号处理函数
应用程序根据驱动程序所使用的信号来设置信号的处理函数,应用程序使用 signal 函数来
置信号的处理函数。

sighandler_t signal(int signum, sighandler_t handler));
第一个参数指定信号的值, 第二个参数指定针对前面信号值的处理函数, 若为 SIG_IGN, 表示忽略该信
号; 若为 SIG_DFL, 表示采用系统默认方式处理信号; 若为用户自定义的函数, 则信号被捕获到后, 该函数
将被执行

2、将本应用程序的进程号告诉给内核
使用 fcntl(fd, F_SETOWN, getpid())将本应用程序的进程号告诉给内核。
3、开启异步通知
使用如下两行程序开启异步通知:
flags = fcntl(fd, F_GETFL); /* 获取当前的进程状态 */
fcntl(fd, F_SETFL, flags | FASYNC); /* 开启当前进程异步通知功能 */
重点就是通过 fcntl 函数设置进程状态为 FASYNC,经过这一步,驱动程序中的 fasync 函
就会执行。

实例


void sigint_handler(int num)
{
    printf("rnSIGINT signal!rn");
    exit(0);
}

int main(void)
{
     signal(SIGINT, sigint_handler);
    while(1);
    return 0;
}

二、驱动层

1 、首先我们需要在驱动程序中定义一个fasync_struct结构体指针变量,

2、如果要使用异步通知,需要在设备驱动中实现file_operations操作集中的fasync函数,此函数格式如下所示:

int (*fasync) (int fd, struct file *filp, int on)

fasync函数里面一般通过调用fasync_helper函数来初始化前面定义的fasync_struct结构体指针,fasync_helper函数原型如下:

int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)

fasync_helper函数的前三个参数就是fasync函数的那三个参数,第四个参数就是要初始化的fasync_struct结构体指针变量。当应用程序通过“fcntl(fd, F_SETFL, flags | FASYNC)”改变fasync标记的时候,驱动程序file_operations操作集中的fasync函数就会执行。

static int xxx_fasync(int fd, struct file *filp, int on)

{

struct xxx_dev *dev = (xxx_dev)filp->private_data;

if (fasync_helper(fd, filp, on, &dev->async_queue) < 0)

return -EIO;

return 0;

}

static struct file_operations xxx_ops = {

......

.fasync = xxx_fasync,

......

};

 

3、在关闭驱动文件的时候需要在file_operations操作集中的release函数中释放fasync_struct,fasync_struct的释放函数同样为fasync_helper,release函数参数参考实例如下:

static int xxx_release(struct inode *inode, struct file *filp)

{

return xxx_fasync(-1, filp, 0); /* 删除异步通知 */

}

 

4、驱动层如何通知应用层

kill_fasync函数

当设备可以访问的时候,驱动程序需要向应用程序发出信号,相当于产生“中断”。kill_fasync函数负责发送指定的信号,kill_fasync函数原型如下所示:

void kill_fasync(struct fasync_struct **fp, int sig, int band)

函数参数和返回值含义如下:

fp:要操作的fasync_struct。

sig:要发送的信号。

band:可读时设置为POLL_IN,可写时设置为POLL_OUT。

返回值:无。

 

 

 

9.4 linux异步I/O

9.4.1 AIO概念与GNU C库 AIO

9.4.1.1 AIO概念

  • 同步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)就提供了这种功能

9.4.1.1 AIO系列API:

  • aio_read–异步读
    • 作用:请求对一个有效的文件描述符进行异步读写操作
      • 请求进行排队之后会立即返回
      • 这个文件描述符可以表示一个文件、套接字,甚至管道
    • 参数aiocb:结构体包含了传输的所有信息,以及为AIO操作准备的用户空间缓存区
    • 返回值
      • 成功:返回0
      • 失败:返回-1,并设置errno的值
int aio_read( struct aiocb *aiocbp );
  • 1
  • aio_write–异步写
    • 作用:请求一个异步写操作
      • 请求进行排队之后会立即返回
      • 这个文件描述符可以表示一个文件、套接字,甚至管道
    • 参数aiocb:结构体包含了传输的所有信息,以及为AIO操作准备的用户空间缓存区
    • 返回值
      • 成功:返回0
      • 失败:返回-1,并设置errno的值
int aio_write( struct aiocb *aiocbp );
  • 1
  • aio_error
    • 作用:确定请求的状态
    • 参数aiocb:结构体包含了传输的所有信息,以及为AIO操作准备的用户空间缓存区
    • 返回值
      • EINPROGRESS:说明请求尚未完成
      • ECANCELED:说明请求被应用程序取消
      • 失败:返回-1,并设置errno的值
int aio_error( struct aiocb *aiocbp );
  • 1
  • aio_return–获得异步操作的返回值
    • 异步 I/O 和标准块 I/O 之间的另外一个区别是不能立即访问这个函数的返回状态,因为并没有阻塞在 read()调用上
    • 在标准的 read()调用中,返回状态是在该函数返回时提供的。但是在异步 I/O 中,我们要使用 aio_return()函数
    • 只有在 aio_error()调用确定请求已经完成(可能成功,也可能发生了错误)之后,才会调用这个函数
    • 参数aiocb:结构体包含了传输的所有信息,以及为AIO操作准备的用户空间缓存区
    • 返回值
      • 成功:返回所传输的字节数
      • 失败:返回-1
ssize_t aio_return( struct aiocb *aiocbp );
  • 1
  • aio_suspend–挂起异步操作,直到异步请求完成为止
    • 作用:挂起(或阻塞)调用进程,直到异步请求完成为止,调用者提供了一个 aiocb 引用列表,其中任何一个完成都会导致 aio_suspend()返回
int aio_suspend( const struct aiocb *const cblist[], int n, const struct timespec *timeout );
  • 1
  • aio_cancel–取消异步请求
    • 作用:允许用户取消对某个文件描述符执行的一个或所有 I/O 请求
    • 要求:
      • 如果要取消一个请求,用户需提供文件描述符和 aiocb 引用
        • 函数返回AIO_CANCELED:请求被成功取消
        • 函数返回AIO_NOTCANCELED:请求完成
      • 如果要取消对某个给定文件描述符的所有请求,用户需要提供这个文件的描述符以及一个对 aiocbp 的 NULL 引用
        • 函数返回AIO_CANCELED:表明所有的请求都取消了
        • 函数返回AIO_NOTCANCELED:表明至少有一个请求没有被取消
        • 函数返回AIO_ALLDONE:表明没有一个请求可以被取消
      • 使用 aio_error()来验证每个 AIO 请求
        • aio_error()返回-1并且设置了errno被设置为ECANCELED:表明某个请求已经被取消了
int aio_cancel( int fd, struct aiocb *aiocbp );
  • 1
  • lio_listio–同时发起多个传输(一次系统调用可以启动大量的I/O操作)
    • 作用:这个函数非常重要,它使得用户可以在一个系统调用(一次内核上下文切换)中启动大量的 I/O 操作
    • 参数
      • mode:可以是 LIO_WAIT 或 LIO_NOWAIT
        • LIO_WAIT 会阻塞这个调用,直到所有的 I/O 都完成为止
        • 在操作进行排队之后,LIO_NOWAIT 就会返回
      • list :是一个 aiocb 引用的列表,最大元素的个数是由 nent 定义的
        • 如果 list 的元素为 NULL,lio_listio()会将其忽略。
int lio_listio( int mode, struct aiocb *list[], int nent, struct sigevent *sig );
  • 1

9.4.2 Linux内核AIO与libaio

  • linux AIO也可以由内核空间实现,异步I/O是linux2.6以后版本内核的标准特性
  • 对于块设备,AIO可以一次性发出大量的read/write调用并且通过通用块层的I/O调度来获得更好的性能,用户也可以减少过多的同步负载
  • 对网络设备而言,在socket层面上,也可以使用AIO,让CPU和网卡的收发充分交叠以改善吞吐性能
  • 用户空间中一般要结合libaio来进行内核AIO的系统调用
 
  1. io_setup( )

  2.  
  3. //Initializes an asynchronous context for the current process

  4.  
  5. io_submit( )

  6.  
  7. //Submits one or more asynchronous I/O operations

  8.  
  9. io_getevents( )

  10.  
  11. //Gets the completion status of some outstanding asynchronous I/O operations

  12.  
  13. io_cancel( )

  14.  
  15. //Cancels an outstanding I/O operation

  16.  
  17. io_destroy( )

  18.  
  19. //Removes an asynchronous context for the current process

  20.  

9.4.3 AIO与设备驱动

  • 用户空间调用io_submit()之后,对应于用户传递的每个iocb结构,内核会生成一个与之对应的kiocb结构
  • 通过is_sync_kiocb判断某kiocb是否为同步I/O请求

    • 如果是返回真,表示为异步I/O请求
  • 字符设备:必须明确应支持AIO(极少数是异步I/O操作)

  • 字符设备驱动程序中file_operations 包含 3 个与 AIO 相关的成员函数,
 
  1. ssize_t (*aio_read) (struct kiocb *iocb, char *buffer, size_t count, loff_t offset);

  2.  
  3. ssize_t (*aio_write) (struct kiocb *iocb, const char *buffer, size_t count, loff_t offset);

  4.  
  5. int (*aio_fsync) (struct kiocb *iocb, int datasync);

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

 

最后

以上就是英勇月光为你收集整理的linux驱动笔记(五):异步通知的全部内容,希望文章能够帮你解决linux驱动笔记(五):异步通知所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(41)

评论列表共有 0 条评论

立即
投稿
返回
顶部