学习ldd3--异步通知(第六章)

来源:互联网 发布:linux重命名文件的命令 编辑:程序博客网 时间:2024/05/19 03:18
作者:张伟AreS
/*******************************************************************************/
代码:D:\学习\个人学习笔记及网络经典文章\学习LDD3笔记\ldd3_examples\scull\main.c
      D:\学习\个人学习笔记及网络经典文章\学习LDD3笔记\ldd3_examples\scull\pipe.c
通过使用异步通知机制,应用程序可以在指定的I/O操作可执行时,收到一个信号,而不需要不停的使用轮询来查询设备。
要使用异步通知机制,对于用户空间程序来说,需要执行如下步骤:
   首先,指定一个进程作为文件的“属主”,这是通过使用fcntl系统调用执行F_SETOWN命令完成的,该命令会把进程ID号保存在filp->f_owner中.
这一步的目的是让内核知道应该通知哪个进程。
   其次,在设备中设置FASYNC标志,这是通过fcntl的F_SETFL命令完成的。
执行完这两个步骤后,当指定的I/O操作可执行时,就会给相应进程发送一个SIGIO信号。
从内核的角度看,要实现异步通知机制,需要经过如下三个步骤:
   首先,F_SETOWN被调用时,对filp->f_owner赋值,此外什么也不做。
   其次,执行F_SETFL设置FASYNC时,调用驱动程序的fasync函数。只要filp->f_flags中的FASYNC标志发生了变化,就应该调用这个函数,以便把这个变化通知驱动程序,
使其能做出正确响应。文件打开时,FASYNC标志默认是被清除的。
   第三,当指定的I/O操作可执行时,所有注册为异步通知的进程都会被发送一个SIGIO信号。
/*************************************/
在用户空间(应用程序)asynctest中,
   void sighandler(int signo)
{
    if (signo==SIGIO)
        gotdata++;//默认为0
    return;
}
   /*设置sighandler函数为信号处理函数,当有信号从驱动来时,调用此函数(sighandler)处理。*/
    struct sigaction action;
    memset(&action, 0, sizeof(action));
    action.sa_handler = sighandler;  /*指定其为信号来时的处理函数*/
    action.sa_flags = 0;
    sigaction(SIGIO, &action, NULL);
     /*在执行F_SETFL启用FASYNC时,调用驱动程序的fasync方法,只要filp->f_flags中的FASYNC标志发生变化时,就会调用该方法
     *以便把这个变化通知驱动程序,文件打开时,FASYNC标志被默认清除的
     *当数据倒倒时,所有注册为异步通知的进程都会被发送一个SIGIO信号
     */
    fcntl(STDIN_FILENO, F_SETOWN, getpid());//进程使用fcntl系统调用执行F_SETOWN命令,属主进程ID号(getpid())被保存在filp->f_owner中。
    fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) | FASYNC);//fcntl通过F_SETFL命令设置FASYNC标志。
在驱动中:
  /*维护一个动态数据结构struct fasync_struct,以跟踪不同的异步读取进程
   *当一个打开的文件的FASYNC标志被修改时,调用fasync_helper以便从相关的进程列表中增加或删除文件。
   *除了最后一个参数外,fasync_helper的其它参数与驱动程序的scull_p_fasync函数相同,可以直接传递。
   */
 
 static int scull_p_fasync(int fd, struct file *filp, int mode)//在scullpipe中这样实现fasync方法
{
 struct scull_pipe *dev = filp->private_data;
 return fasync_helper(fd, filp, mode, &dev->async_queue);//初始化dev->async_queue即struct fasync_struct结构
}
  /*当指定的I/O操作可执行时,应使用kill_fasync通知所有的相关进程,
   *该函数的第二个参数是要发送的信号(通常是SIGIO),第三个参数是带宽(通常是POLL_IN)。
   *由于提供给scullpipe的读取进程的新数据是由某个进程调用write产生的,
   *所以kill_fasync函数在scullpipe的write函数中调用
  */
    if (dev->async_queue)//此处可去掉,在write函数中之前已判断是否有数据,仅仅去判断struct fasync_struct结构是否已建立
      kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
  
  //如果是针对写入的异步通知,kill_fasync的第三个参数必须为POLL_OUT。从异步通知的列表中删除该filp
  scull_p_fasync(-1,filp,0);
  /*即:*/
  return fasync_helper(-1, filp, 0, &dev->async_queue);

附加用户空间的asynctest源代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
int gotdata=0;
void sighandler(int signo)
{
    if (signo==SIGIO)
        gotdata++;
    return;
}
char buffer[4096];
int main(int argc, char **argv)
{
    int count;
    struct sigaction action;
    memset(&action, 0, sizeof(action));
    action.sa_handler = sighandler;
    action.sa_flags = 0;
    sigaction(SIGIO, &action, NULL);
    fcntl(STDIN_FILENO, F_SETOWN, getpid());
    fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) | FASYNC);
    while(1) {
        /* this only returns if a signal arrives */
        sleep(86400); /* one day */
        if (!gotdata)
            continue;
        count=read(0, buffer, 4096);
        /* buggy: if avail data is more than 4kbytes... */
        write(1,buffer,count);
        gotdata=0;
    }
}
原创粉丝点击