linux驱动的异步同步通知机制

来源:互联网 发布:js 当前页面url 编辑:程序博客网 时间:2024/05/29 12:53

转载:http://blog.csdn.net/z1106609486/article/details/51461058

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

9.1 异步通知的概念和作用

  • 异步通知:一旦设备就绪,则主动通知应用程序,该应用程序无需查询设备状态
  • 几种通知方式比较: 
    • 阻塞I/O :一直等待设备可访问后开始访问
    • 非阻塞I/O:使用poll()查询设备是否访问
    • 异步通知 :设备主动通知用户应用程序-

9.2 linux异步通知编程

9.2.1 linux信号

  • 作用:linux系统中,异步通知使用信号来实现
  • Linux信号及其定义如下:
信号值含义SIGHUP1挂起SIGINT2终端中断SIGQUIT3终端退出SIGILL4无效命令SIGTRAP5跟踪陷阱SIGIOT6IOT陷阱SIGBUS7BUS错误SIGFPE8浮点异常SIGKILL9强行终止SIGUSR110用户定义信号1SIGSEGV11无效的内存段处理SIGUSR212用户定义信号2SIGPIPE13半关闭管道的写操作已经发生SIGALRM14计时器到时SIGTERM15终止进程SIGSTKFLT16堆栈错误SIGCHLD17子进程已经停止或退出SIGCONT18如果停止了,继续执行SIGSTOP19停止执行SIGTSTP20终端来的停止信号SIGTTIN21后台进程读终端SIGTTOU22后台进程写终端SIGURG23I/O紧急信号SIGXGPU24CPU时限超时SIGXFSZ25文件长度过长SIGVTALRM26虚拟计时器到时SIGPROF27统计分布图用计时器到时SIGWINCH28窗口大小发生变化SIGIO29描述符上可以进行I/OSIGPWR30断电重启

9.2.2 信号的接收

  • 信号捕获函数signal() 
    • 参数: 
      • signum:信号值
      • handler:针对signum的处理函数 
        • 若为SIG_IGN:忽略该信号
        • 若为SIG_DFL:系统默认方式处理
        • 若为用户自定义函数:信号被捕获,该函数被执行
    • 返回值 
      • 成功:最后一次为信号signum绑定的处理函数的handler值
      • 失败:返回SIG_ERR
    • sigaction() 
      • 作用:改变进程接收到特定信号后的行为
      • 参数 
        • signum:信号值 
          • 除SIG_KILL及SIG_STOP以外的一个特定有效的信号
        • act:指向结构体sigaction的一个实例的指针 
          • 在结构体sigaction中,指定了处理信号的函数,若为空则进程会以缺省值的方式处理信号
        • oldact:保存原来对应的信号的处理函数,可设为NULL
int sigaction(int signo,const struct sigaction *restrict act, struct sigaction *restrict oact);
  • 1
  • 1
  • 实例:使用信号实现异步通知 
    • 在用户空间处理设备释放信号的准备工作 
      • 通过F_SETOWN IO控制命令设置设备文件的拥有者为本进程,以使信号被本进程捕获
      • 通过F_SETFL IO控制命令设置设备文件以支持FASYNC,及异步通知模式
      • 通过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:%s\n",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;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

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

  • 为了使设备支持异步通知机制,驱动程序中涉及以下3项工作 
    • 支持F_SETOWN命令,能在这个控制命令处理中设置filp->f_owner为对应的进程ID。不过此项工作已由内核完成,设备驱动无须处理
    • 支持F_SETFL命令处理,每当FASYNC标志改变时,驱动函数中的fasync()函数得以执行。因此,驱动中应该实现fasync()函数
    • 在设备资源中可获得,调用kill_fasync()函数激发相应的信号
  • 设备驱动中异步通知编程: 
    • 处理FASYNC标志变更函数:fasync_helper()
    • 释放信号的函数:kill_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);
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3
  • 将fasync_struct结构体指针放到设备结构体中是最佳的选择
//异步通知的设备结构体模板struct xxx_dev{    struct cdev cdev;    ...    struct fasync_struct *async_queue;//异步结构体指针};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 在设备驱动中的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);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 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);    ...}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 最后在文件关闭时,要将文件从异步通知列表中删除
int xxx_release(struct inode *inode,struct file *filp){    //将文件从异步通知列表中删除    xxx_fasync(-1,filp,0);    ...    return 0;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

9.3 linux异步I/O

9.3.1 AIO概念与GNU C库 AIO

9.3.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.3.1.2 AIO系列API:

  • aio_read–异步读 
    • 作用:请求对一个有效的文件描述符进行异步读写操作 
      • 请求进行排队之后会立即返回
      • 这个文件描述符可以表示一个文件、套接字,甚至管道
    • 参数aiocb:结构体包含了传输的所有信息,以及为AIO操作准备的用户空间缓存区
    • 返回值 
      • 成功:返回0
      • 失败:返回-1,并设置errno的值
int aio_read( struct aiocb *aiocbp );
  • 1
  • 1
  • aio_write–异步写 
    • 作用:请求一个异步写操作 
      • 请求进行排队之后会立即返回
      • 这个文件描述符可以表示一个文件、套接字,甚至管道
    • 参数aiocb:结构体包含了传输的所有信息,以及为AIO操作准备的用户空间缓存区
    • 返回值 
      • 成功:返回0
      • 失败:返回-1,并设置errno的值
int aio_write( struct aiocb *aiocbp );
  • 1
  • 1
  • aio_error 
    • 作用:确定请求的状态
    • 参数aiocb:结构体包含了传输的所有信息,以及为AIO操作准备的用户空间缓存区
    • 返回值 
      • EINPROGRESS:说明请求尚未完成
      • ECANCELED:说明请求被应用程序取消
      • 失败:返回-1,并设置errno的值
int aio_error( struct aiocb *aiocbp );
  • 1
  • 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
  • 1
  • aio_suspend–挂起异步操作,直到异步请求完成为止 
    • 作用:挂起(或阻塞)调用进程,直到异步请求完成为止,调用者提供了一个 aiocb 引用列表,其中任何一个完成都会导致 aio_suspend()返回
int aio_suspend( const struct aiocb *const cblist[], int n, const struct timespec *timeout );
  • 1
  • 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
  • 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
  • 1

9.3.2 Linux内核AIO与libaio

  • linux AIO也可以由内核空间实现,异步I/O是linux2.6以后版本内核的标准特性
  • 对于块设备,AIO可以一次性发出大量的read/write调用并且通过通用块层的I/O调度来获得更好的性能,用户也可以减少过多的同步负载
  • 对网络设备而言,在socket层面上,也可以使用AIO,让CPU和网卡的收发充分交叠以改善吞吐性能
  • 用户空间中一般要结合libaio来进行内核AIO的系统调用
io_setup( )//Initializes an asynchronous context for the current processio_submit( )//Submits one or more asynchronous I/O operationsio_getevents( )//Gets the completion status of some outstanding asynchronous I/O operationsio_cancel( )//Cancels an outstanding I/O operationio_destroy( )//Removes an asynchronous context for the current process
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

9.3.3 AIO与设备驱动

  • 用户空间调用io_submit()之后,对应于用户传递的每个iocb结构,内核会生成一个与之对应的kiocb结构
  • 通过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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5
  • 块设备和网络设备:本身是异步的
0 0
原创粉丝点击