现在的位置: 首页 > 综合 > 正文

fasync 异步通知

2017年05月19日 ⁄ 综合 ⁄ 共 3738字 ⁄ 字号 评论关闭

 fasync异步通知

 

更多信息请看:http://blog.csdn.net/mirkerson/article/details/7443907

                           http://hi.baidu.com/decload/item/713bffa94c17c8991410737e

 

这里讲的异步通知,是指设备数据就绪了(可读或可写),驱动可以发送一个信号给进程(该文件的属主进程)。通过使用异步通知,无论何时数据可用,应用程序可以接收到一个信号并且不需要让自己去轮询。

 

异步通知的功能需要应用和驱动完成一些步骤。

⒈ 应用程序应做的工作

⑴ 注册信号处理函数。

      
通过signal sigaction()实现。

⑵ 使进程成为该文件的的属主进程。

      
 通过fcntl F_SETOWN命令来实现。如fcntl(fd, F_SETOWN,getpid());

⑶ 启用异步通知功能。

      
通过fcntl F_SETFL命令设置FASYNC标记。

例如,
下面的用户程序中的代码行使能了异步的通知到当前进程, stdin输入文件:

---------------------------------------------------------------------

signal(SIGIO, &input_handler); /* dummy sample; sigaction() is better*/

fcntl(STDIN_FILENO, F_SETOWN, getpid());

oflags = fcntl(STDIN_FILENO, F_GETFL);

fcntl(STDIN_FILENO, F_SETFL, oflags | FASYNC);

---------------------------------------------------------------------

 

 

 

 

. 驱动应做的工作

在内核里异步通知是通过,异步通知队列struct fasync_struct来实现的。在这里,我们只需要用到内核提供的两个有关异步通知队列的函数就可以了。fasync_helper
kill_fasync

 

fasync_helper用于把文件指针加到(或移除,当参数on =
0
)异步通知队列

kill_fasync用于发送信号给应用程序。

 

---------------------------------------------------------------------

fs/fcntl.c

int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct**fapp)

{

   if (!on)

      returnfasync_remove_entry(filp, fapp);

   return fasync_add_entry(fd, filp,fapp);

}

---------------------------------------------------------------------

 

 

 

---------------------------------------------------------------------

fs/fcntl.c

void kill_fasync(struct fasync_struct **fp, int sig, int band)

{

   /* First a quick test withoutlocking: usually

    * the list is empty.

    */

   if (*fp) {

      rcu_read_lock();

      kill_fasync_rcu(rcu_dereference(*fp),sig, band);

      rcu_read_unlock();

   }

}

---------------------------------------------------------------------

 

具体的字符设备驱动通过下面这几个步骤来实现:

⑴ 实现structfile_operations.fasync函数。

这个函数,会在应用程序里,设置/删除FASYNC文件标记时会调用。该函数指针的原型为:

int (*fasync)(intfd, struct file *filp, int mode) ;

在函数实现时,我们只需要调用:

int fasync_helper(int fd, struct file *filp, int on, struct fasync_struct **fapp) ;

⑵在数据就绪时,调用kill_fasync通知应用程序

⑶ 在用户关闭文件(realease函数里)时,把该文件指针从异步通知队列里删除。同样也是调用fasync_helper

 

 

fasync简介

异步通知fasync应用于系统调用signal和sigaction函数,简单的说,signal函数就是让一个信号与与一个函数对应,每当接收到这个信号就会调用相应的函数。
那么什么是异步通知?异步通知类似于中断的机制,当设备可写时,设备驱动函数发送一个信号给内核,告知内核有数据可读,在条件不满足之前,并不会造成阻塞。而不像之前学的阻塞型IO和poll,它们是调用函数进去检查,条件不满足时还会造成阻塞。

应用层中启用异步通知机制

其实在应用层启用异步通知只三个步骤:
1)signal(SIGIO, sig_handler);
调用signal函数,让指定的信号SIGIO与处理函数sig_handler对应。
2)fcntl(fd, F_SET_OWNER, getpid());
指定一个进程作为文件的“属主(filp->owner)”,这样内核才知道信号要发给哪个进程。
3)f_flags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, f_flags | FASYNC);
在设备文件中添加FASYNC标志,驱动中就会调用将要实现的test_fasync函数。
三个步骤执行后,一旦有信号产生,相应的进程就会收到。

驱动中需要实现的异步通知

3.1 异步通知内核实现

实现异步通知,内核需要知道几个东西:哪个文件(filp),什么信号(SIGIIO),发给哪个进程(pid),收到信号后做什么(sig_handler)。这些都由上述前两个步骤完成了,而这前两个步骤内核帮忙实现了,所以,我们只需要实现第三个步骤的一个简单的传参。

3.2 fasync_struct结构体

要实现传参,我们需要把一个结构体struct fasync_struct添加到内核的异步队列中,这个结构体用来存放对应设备文件的信息(如fd, filp)并交给内核来管理。一但收到信号,内核就会在这个所谓的异步队列头找到相应的文件(fd),并在filp->owner中找到对应的进程PID,并且调用对应的sig_handler了。
struct fasync_struct {
int magic;
int fa_fd;
struct fasync_struct *fa_next; /* singly linked list */
struct file *fa_file;
};

3.3 内核中我们的工作

上面说了前两个步骤会由内核完成,所以我们只要做两件事情:
1)定义结构体fasync_struct。
struct fasync_struct *async_queue;
2)实现test_fasync,把函数fasync_helper将fd,filp和定义的结构体传给内核。
int test_fasync (int fd, struct file *filp, int mode)
{
struct _test_t *dev = filp->private_data;
return fasync_helper(fd, filp, mode, &dev->async_queue);
}
函数fasync_helper的定义为:
int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)
前面的三个参数其实就是teat_fasync的三个参数,所以只要我们定义好的fasync_struct结构体也传进去就可以了。

3.4 其余的工作

另外还有两件事:
3)当设备可写时,调用函数kill_fasync发送信号SIGIO给内核。
if (dev->async_queue){
kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
}
讲解一下这个函数:
void kill_fasync(struct fasync_struct **fp, int sig, int band)
sig就是我们要发送的信号。
band(带宽),一般都是使用POLL_IN,表示设备可读,如果设备可写,使用POLL_OUT
4)当设备关闭时,需要将fasync_struct从异步队列中删除:
test_fasync(-1, filp, 0);
删除也是调用test_fasync,不过改了一下参数而已。

 

 

抱歉!评论已关闭.