驱动对异步IO的实现
用一句话描述:就是一旦设备就绪(比如发生了某个硬件中断,或者得到了某个资源),则驱动层向应用程序发送信号,触发应用程序的信号服务函数
使用这种方法,应用层就可以不用一直去查询设备状态,这其实是软件层次上对中断机制的一种模拟
1.应用层接收信号
- 异步通知是基于信号的,信号还可以用于进程间通信。Linux中的信号有很多,具体可以去查信号表,除了SIGSTOP 和 SIGKILL 两个信号外(kill -9 就是利用了SIGKILL这个信号),我们可以设置进程忽略或捕获其他的全部信号
- 要让进程支持异步通知,做一些简单的设置即可
/*定义一个信号服务函数*/
void sigterm_handler(int signo)
{
...
}
/*可以在main函数中做如下设置*/
...
signal(SIGIO, xxx_handler); /* 让xxx_handler()处理 SIGIO 信号 */
/*设置设备文件的拥有者为本进程,即设置了设备文件将向本进程发信号*/
fcntl(fd, F_SETOWN, getpid());
/*设置flag为FASYNC,使设备文件支持异步通知*/
oflags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, oflags | FASYNC);
...
- signal接口用来绑定信号和信号服务函数,此处我们选用SIGIO信号。值得注意的是,多个不同的信号可以绑定同一个服务函数,这也是为什么服务函数有signo这个参数的原因,当信号服务函数被触发时,可以通过signo得知是哪个信号触发了自己
- 利用fcntl设置设备文件的拥有者为本进程,即设置了设备文件将向本进程发信号
- 最后设置设备文件的flag为FASYNC,使设备文件支持异步通知
2.驱动层发送信号
- 首先定义异步结构体指针,一般是全局变量,被定义在驱动程序中device的结构体中
struct fasync_struct *async_queue; /* 异步结构体指针 */
- 然后还需要提供file operation中的.fasync操作,这个函数很简单,其实就是一层皮,我们只要调用fasync_helper接口即可
static int xxx_fasync(int fd, struct file *filp, int mode)
{
return fasync_helper(fd, filp, mode, async_queue);
}
- 当资源到位时,发送信号,这一步很多时候在硬件中断中完成。此处我们选用SIGIO信号,可读时第 3 个参数设置为POLL_IN,可写时第 3 个参数设置为 POLL_OUT
/*发信号*/
if (async_queue){
kill_fasync(async_queue, SIGIO, POLL_IN);
}
- 最后,在文件关闭时,即在设备驱动的 release()函数中,应调用设备驱动的 fasync()函数将文件从异步通知的列表中删除
static int xxx_release(struct inode *inode, struct file *filp)
{
/* 将文件从异步通知列表中删除 */
xxx_fasync(-1, filp, 0);
}