1. 上一节我们已经学习了用poll轮询数据,来避免不必要的休眠,但是事实上,轮询的直接负面作用就是效率低下,这样一节我们学习如何使用异步通知IO来提高效率
2. fcntl系统调用
int fcntl(int fd, int cmd, long arg);
fcntl的作用是改变一个已打开文件的属性,fd是要改变的文件的描述符,cmd是命令罗列如下:
F_DUPFD, F_GETFD, F_SETFD, F_GETFL, F_SETFL, F_SETLK, F_SETLKW, F_GETLK, F_GETOWN, F_SETOWN
本节只关心F_SETOWN(设置异步IO所有权),F_GETFL(获取文件flags),F_SETFL(设置文件flags)
arg是要改变的属性内容
3. 用户进程启用异步通知机制
首先,设置一个进程作为一个文件的属主(owner),这样内核就知道该把文件的信号发送给哪个进程
fcntl(fd, F_SETOWN, getpid()); // getpid()就是当前进程咯
然后,给文件设置FASYNC标志,以启用异步通知机制
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | FASYNC);
4. 缺陷
当有多个文件发送异步通知信号给一个进程时,进程无法知道是哪个文件发送的信号,这时候还是要借助poll的帮助完成IO
5. 从驱动程序的角度考虑
当文件的状态标志设置了FASYNC操作时,驱动程序会调用fasync的函数。
fasync的实现相当简单
static int scull_p_fasync(int fd, struct file *filp, int mode)
{
struct scull_pipe *dev = filp->private_data;
return fasync_helper(fd, filp, mode, &dev->async_queue);
}
当有新的数据到达时,驱动程序应该发送一个SIGIO给用户,这个操作用kill_fasync方法完成
if(dev->async_queue)
kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
最后,从异步通知列表中移除注册进去了的文件指针就直接调用scull_p_fasync(-1, filp, 0);