情形分析:一个进程需要从外设读取采集到的数据进行处理,如果有新的数据到来,应用程序可以周期性地调用poll来检查数据,但是这样做的效率并不高。因此,通过使用异步通知,应用程序可以在数据可用时收到一个信号,转而进行处理,而不需要不停的使用查询的方式来检查有无新数据的到来。
为了启用文件的异步通知机制,应用程序执行以下三个步骤:
step1:指定一个进程作为文件的“属主(owner)”(在file结构中讲到)。当进程使用fcntl系统调用执行F_SETOWN命令时,属主进程的进程ID号就被保持在filp->f_owner中,目的是为了让内核知道应该通知哪个进程。
step2:应用程序还必须在设备中设置FASYNC标志,通过使用fcntl的F_SETFL命令。
step3:输入文件就可以在新数据到达时请求发送一个SIGIO信号。该信号被发送到存放在flip->f_owner中的进程。
signal(SIGIO, &input_handler);
fcntl(STDIN_FILENO, F_SETOWN, getpid());
oflags = fcntl(STDIN_FILENO, F_GETFL);
fcntl(STDIN_FILENO, F_SETFL, oflags | FASYNC);
从驱动程序的角度考虑
1.F_SETOWN被调用时对filp->f_owner赋值
2.执行F_SETFL启动FASYNC时,调用驱动程序的fasync方法。只要filp->f_flags中的FASYNC标志发生了变化,就会调用该方法。在文件打开时,FASYNC标志默认是清除的。
3.当数据到达时,所有注册为异步通知的进程都会被发送一个SIGIO信号。
异步通知执行的通用方法涉及一个结构体和两个函数,在<linux/fs.h>文件中,结构体声明为struct fasync_struct。和等待队列的方式类似,需要把一个该类型的指针插入设备特定的数据结构中。驱动程序需要调用的两个函数
对于大多数字符设备驱动建立异步队列调用该函数,当一个打开的文件的FASYNC标志被修改时,调用fasync_helper从相关的进程列表中增加或删除文件
int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)
{
if (!on)
return fasync_remove_entry(filp, fapp);
return fasync_add_entry(fd, filp, fapp);
}
如果没有无改变返回0,添加或删除成功返回正数,错误时返回负数。
void kill_fasync(struct fasync_struct **fp, int sig, int band)
{
/* First a quick test without locking: usually
* the list is empty.
*/
if (*fp) {
rcu_read_lock();
kill_fasync_rcu(rcu_dereference(*fp), sig, band);
rcu_read_unlock();
}
}
当数据到达时,可使用kill_fasync通知所有的相关进程。