转载: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信号及其定义如下:
信号 | 值 | 含义 |
---|---|---|
SIGHUP | 1 | 挂起 |
SIGINT | 2 | 终端中断 |
SIGQUIT | 3 | 终端退出 |
SIGILL | 4 | 无效命令 |
SIGTRAP | 5 | 跟踪陷阱 |
SIGIOT | 6 | IOT陷阱 |
SIGBUS | 7 | BUS错误 |
SIGFPE | 8 | 浮点异常 |
SIGKILL | 9 | 强行终止 |
SIGUSR1 | 10 | 用户定义信号1 |
SIGSEGV | 11 | 无效的内存段处理 |
SIGUSR2 | 12 | 用户定义信号2 |
SIGPIPE | 13 | 半关闭管道的写操作已经发生 |
SIGALRM | 14 | 计时器到时 |
SIGTERM | 15 | 终止进程 |
SIGSTKFLT | 16 | 堆栈错误 |
SIGCHLD | 17 | 子进程已经停止或退出 |
SIGCONT | 18 | 如果停止了,继续执行 |
SIGSTOP | 19 | 停止执行 |
SIGTSTP | 20 | 终端来的停止信号 |
SIGTTIN | 21 | 后台进程读终端 |
SIGTTOU | 22 | 后台进程写终端 |
SIGURG | 23 | I/O紧急信号 |
SIGXGPU | 24 | CPU时限超时 |
SIGXFSZ | 25 | 文件长度过长 |
SIGVTALRM | 26 | 虚拟计时器到时 |
SIGPROF | 27 | 统计分布图用计时器到时 |
SIGWINCH | 28 | 窗口大小发生变化 |
SIGIO | 29 | 描述符上可以进行I/O |
SIGPWR | 30 | 断电重启 |
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
- signum:信号值
- 参数:
- 1
- 1
- 实例:使用信号实现异步通知
- 在用户空间处理设备释放信号的准备工作
- 通过F_SETOWN IO控制命令设置设备文件的拥有者为本进程,以使信号被本进程捕获
- 通过F_SETFL IO控制命令设置设备文件以支持FASYNC,及异步通知模式
- 通过signal()函数连接信号和信号处理函数
- 在用户空间处理设备释放信号的准备工作
- 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()
- 1
- 2
- 3
- 1
- 2
- 3
- 将fasync_struct结构体指针放到设备结构体中是最佳的选择
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
- 在设备驱动中的fasync()函数中,只需简单地将该函数的3个参数以及fasync_struct结构体指针的指针作为第四个参数传入fasync_helper()函数就可以了,模板如下
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
- 在设备资源可获得时应该调用kill_fasync()函数释放SIGIO信号,可读时第三个参数为POLL_IN,可写时第三个参数为POLL_OUT,模板如下
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 最后在文件关闭时,要将文件从异步通知列表中删除
- 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的值
- 作用:请求对一个有效的文件描述符进行异步读写操作
- 1
- 1
- aio_write–异步写
- 作用:请求一个异步写操作
- 请求进行排队之后会立即返回
- 这个文件描述符可以表示一个文件、套接字,甚至管道
- 参数aiocb:结构体包含了传输的所有信息,以及为AIO操作准备的用户空间缓存区
- 返回值
- 成功:返回0
- 失败:返回-1,并设置errno的值
- 作用:请求一个异步写操作
- 1
- 1
- aio_error
- 作用:确定请求的状态
- 参数aiocb:结构体包含了传输的所有信息,以及为AIO操作准备的用户空间缓存区
- 返回值
- EINPROGRESS:说明请求尚未完成
- ECANCELED:说明请求被应用程序取消
- 失败:返回-1,并设置errno的值
- 1
- 1
- aio_return–获得异步操作的返回值
- 异步 I/O 和标准块 I/O 之间的另外一个区别是不能立即访问这个函数的返回状态,因为并没有阻塞在 read()调用上
- 在标准的 read()调用中,返回状态是在该函数返回时提供的。但是在异步 I/O 中,我们要使用 aio_return()函数
- 只有在 aio_error()调用确定请求已经完成(可能成功,也可能发生了错误)之后,才会调用这个函数
- 参数aiocb:结构体包含了传输的所有信息,以及为AIO操作准备的用户空间缓存区
- 返回值
- 成功:返回所传输的字节数
- 失败:返回-1
- 1
- 1
- aio_suspend–挂起异步操作,直到异步请求完成为止
- 作用:挂起(或阻塞)调用进程,直到异步请求完成为止,调用者提供了一个 aiocb 引用列表,其中任何一个完成都会导致 aio_suspend()返回
- 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:表明某个请求已经被取消了
- 如果要取消一个请求,用户需提供文件描述符和 aiocb 引用
- 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()会将其忽略。
- mode:可以是 LIO_WAIT 或 LIO_NOWAIT
- 1
- 1
9.3.2 Linux内核AIO与libaio
- linux AIO也可以由内核空间实现,异步I/O是linux2.6以后版本内核的标准特性
- 对于块设备,AIO可以一次性发出大量的read/write调用并且通过通用块层的I/O调度来获得更好的性能,用户也可以减少过多的同步负载
- 对网络设备而言,在socket层面上,也可以使用AIO,让CPU和网卡的收发充分交叠以改善吞吐性能
- 用户空间中一般要结合libaio来进行内核AIO的系统调用
- 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 相关的成员函数
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
- 块设备和网络设备:本身是异步的