驱动篇:异步通知与异步 I/O(二)
1.信号的释放
在设备驱动和应用程序的异步通知交互中,仅仅在应用程序端捕获信号是不够的,因为信号没有的源头在设备驱动端。因此,应该在合适的时机让设备驱动释放信号,在设备驱动程序中增加信号释放的相关代码。为了使设备支持异步通知机制,驱动程序中涉及以下 3 项工作:
l 支持 F_SETOWN 命令,能在这个控制命令处理中设置 filp->f_owner 为对应
进程 ID。不过此项工作已由内核完成,设备驱动无须处理。
l 支持 F_SETFL 命令的处理,每当 FASYNC 标志改变时,驱动程序中的 fasync()
函数将得以执行。因此,驱动中应该实现 fasync()函数。
l 在设备资源可获得时,调用 kill_fasync()函数激发相应的信号。
驱动中的上述 3 项工作和应用程序中的 3 项工作是一一对应的,图 9.2 所示为异步通知处理过程中用户空间和设备驱动的交互
设备驱动中异步通知编程比较简单,主要用到一项数据结构和两个函数。数据结构是 fasync_struct 结构体,两个函数分别如下
处理 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);
和其他的设备驱动一样,将 fasync_struct 结构体指针放在设备结构体中仍然是最佳选择,代码清单 9.3 给出了支持异步通知的设备结构体模板。
struct xxx_dev
{
struct cdev cdev; /*cdev 结构体*/
...
struct fasync_struct *async_queue; /* 异步结构体指针 */
};
在 设 备 驱 动 的 fasync() 函 数 中 , 只需要简单地将该函 数 的 3 个参数以及fasync_struct 结构体指针的指针作为第 4 个参数传入 fasync_helper()函数即可。
支持异步通知的设备驱动 fasync()函数的模板
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);
}
在设备资源可以获得时,应该调用 kill_fasync()释放 SIGIO 信号,可读时第 3 个参数设置为 POLL_IN,可写时第 3 个参数设置为 POLL_OUT。
static ssize_t xxx_write(struct file *filp, const char _ _user *buf,
size_t count,loff_t *f_pos)
{
struct xxx_dev *dev = filp->private_data;
...
/* 产生异步读信号 */
if (dev->async_queue)
kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
...
}
最后,在文件关闭时, 即在设备驱动的 release()函数中,应调用设备驱动的 fasync()
函数将文件从异步通知的列表中删除。代码清单 9.6 给出了支持异步通知的设备驱动
release()函数的模板。
最后,在文件关闭时, 即在设备驱动的 release()函数中,应调用设备驱动的 fasync()函数将文件从异步通知的列表中删除
static int xxx_release(struct inode *inode, struct file *filp)
{
struct xxx_dev *dev = filp->private_data;
/* 将文件从异步通知列表中删除 */
xxx_fasync(-1, filp, 0);
...
return 0;
}