Linux 设备驱动学习之 异步通知

转载 2012年06月16日 13:06:31

From:http://blog.csdn.net/ypoflyer/article/details/6131334

 

异步通知:

 

使用poll轮询方式的时候,相当于应用程序在需要的时候询问设备“准备好了吗?”,如果有这样一种情况,一个进程在低优先级正在执行长的循环计算,但又需要“尽可能快”的处理输入数据,如果采用poll的方式,那么需要这个应用程序周期性的调用poll来检测数据,也就是周期性的询问设备“准备好了吗?”,显然这种情况下poll并不是最佳的方法。更好的方法应该是一旦设备准备好了就发出一个“我准备好了”的信号给应用程序,然后应用程序再去处理。这样显然更高效。这种方法就是:异步通知。

 


通过上面的描述,显然如果想要异步通知,首先设备就要有发信号的功能,这就要启动文件的异步通知机制。为了启动这个机制,用户程序需要进行两个步骤:
1.指定一个进程作为文件的owner(“所有者”或者“属主”),用来接收“我准备好了”这个信号。该进程的ID好被保存到filp->f_owner中。通过调用fcntl执行F_SETOWN来完成这一步。
2.在设备中设置FASYNC标志来真正启动异步通知机制。通过调用fcntl执行F_SETFL命令完成这一步。

 


执行完这两个步骤以后,当有新数据到达时设备文件会发送一个SIGIO信号(“我准备好了”),该信号被发送到存放在file->f_owner中的进程。
注:显然进程如果只接受到“我准备好了”这个信号,它就会问“那个‘我’是谁阿”,所以如果有多于一个文件可以异步通知输入的进程,那么当应用程序接收到信号时还是要借助poll轮询一次,以便真正确定输入的来源。


下面分别从驱动程序角度和应用程序角度分别总结一下我们应该进行哪些工作。
一、 驱动方面:
1. 在设备抽象的数据结构中增加一个struct fasync_struct的指针
2. 实现设备操作中的fasync函数,其主体就是调用内核的fasync_helper函数。
3. 在需要向用户空间通知的地方(例如scullpipe的write中)调用内核的kill_fasync函数。
4. 在驱动的release方法中调用前面定义的fasync函数, 例如scull_p_fasync(-1, filp, 0);


看一下驱动方面的源码:


    //对应上边的第1步
    struct scull_pipe   

  1.     {  
  2.         ......  
  3.         struct fasync_struct *async_queue;   
  4.         ......  
  5.     };  

 

    //对应上面的第2步
static int scull_p_fasync(int fd, struct file *filp, int mode)   

  1.     {  
  2.         struct scull_pipe *dev = filp->private_data;  
  3.   
  4.         return fasync_helper(fd, filp, mode, &dev->async_queue);  
  5.     }  
    
    //对应上面的第3步,由于新数据是由于进程调用了write而产生的,所以在这里发送SIGIO信号
static ssize_t scull_p_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)  
  1.     {  
  2.         ......  
  3.         ......  
  4.         if (dev->async_queue)   
  5.             kill_fasync(&dev->async_queue, SIGIO, POLL_IN);  
  6.         ......  
  7.         ......  
  8.   
  9.     }  
    
    //对应上面第4步
  1. static int scull_p_release(struct inode *inode, struct file *filp)  
  2.     {  
  3.         ......  
  4.         scull_p_fasync(-1, filp, 0);  
  5.         ......  
  6.     }  

 

 


二、 应用层方面
1. 利用signal或者sigaction设置SIGIO信号的处理函数,关于signal的使用参照http://blog.csdn.net/ypoflyer/article/details/6131334

2. fcntl的F_SETOWN指令设置当前进程为设备文件owner
3. fcntl的F_SETFL指令设置FASYNC标志


看一下应用层方面的源代码,来自asynctest.c文件
   

#include <stdio.h>  
  1. #include <stdlib.h>  
  2. #include <string.h>  
  3. #include <unistd.h>  
  4. #include <signal.h>  
  5. #include <fcntl.h>  
  6.   
  7. int gotdata=0;  
  8. void sighandler(int signo) //这个就是收到SIGIO信号对应的处理函数  
  9. {  
  10.     if (signo==SIGIO)  
  11.         gotdata++;  
  12.     return;  
  13. }  
  14.   
  15. char buffer[4096];  
  16.   
  17. int main(int argc, char **argv)  
  18. {  
  19.     int count;  
  20.     struct sigaction action; //sigaction结构变量action  
  21.   
  22.     memset(&action, 0, sizeof(action)); //清空该变量  
  23.     action.sa_handler = sighandler;  
  24.     action.sa_flags = 0;  
  25.   
  26.     //对应上面的第1步,设置SIGIO的处理函数为sighandle  
  27.     sigaction(SIGIO, &action, NULL); r  
  28.   
  29.     //对应上面的第2步,设置当前进程为设备文件owner。使用getpid获得当前进程的ID  
  30.     fcntl(STDIN_FILENO, F_SETOWN, getpid());  
  31.   
  32.     //对应上面的第3步,设置FASYNC标志,使用的命令是F_SETFL  
  33.     //一旦文件描述符被设置成具有FASYNC属性的状态,  
  34.     //也就是将设备文件切换到异步操作模式。  
  35.     //这时系统就会自动调用驱动程序的fasync方法  
  36.     fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) | FASYNC);  
  37.     //到此异步通知机制就已经设置好了  
  38.       
  39.     while(1) {  
  40.         /* this only returns if a signal arrives */  
  41.         sleep(86400); //此处线程开始休眠,该程序一直处于休眠状态,当有标准输入时候,linux操作系统发送SIGIO消息给进程,并将其唤醒  
  42.         if (!gotdata) //唤醒之后还要再检查以下gotdata的状态,如果不是SIGIO唤醒的,则表明没有标准输入,则继续进行while循环  
  43.             continue;  
  44.         count=read(0, buffer, 4096);  
  45.         /* buggy: if avail data is more than 4kbytes... */  
  46.         write(1,buffer,count);  
  47.         gotdata=0;  
  48.     }  
  49. }  

 

 

如何学好linux设备驱动

Linux系统目前主要维护2.4和2.6两个内核版本,在http://www.kernel.org/ 网站上已经可以下载到最新的2.6内核linux-2.6.38.6,及最新的2.4内核linux-2...
  • shuilaner_
  • shuilaner_
  • 2017年01月10日 16:32
  • 1418

设备驱动学习笔记(2)----学习目录

字符设备模块化编程调试技术并发资源存取
  • wbw1985
  • wbw1985
  • 2010年03月03日 19:57
  • 421

linux驱动的异步同步通知机制

转载:http://blog.csdn.net/z1106609486/article/details/51461058 在设备驱动中使用异步通知可以使得对设备的访问可进行时,由驱动主动通知...
  • A8316124
  • A8316124
  • 2017年02月21日 22:10
  • 421

Linux的fasync驱动异步通知详解

首先还是先从init函数来总结:该驱动是一混杂设备驱动模型来写的,这个主要是借鉴网上的好多资料都是一这种模式来写的,Linux里面misc混杂设备驱动的主设备号是为10的驱动设备,init模块首先是用...
  • coding__madman
  • coding__madman
  • 2016年07月07日 17:43
  • 2448

linux下使用异步通知

阻塞式I/O是一直等待直到设备可以访问,非阻塞式I/O是定期轮询设备是否可以访问。 异步通知则是当设备可以访问时才主动通知应用程序,有点像设备的硬中断。   并不是所有的设备都支持异步通知,应用程序通...
  • zhangskd
  • zhangskd
  • 2015年05月23日 22:47
  • 4790

Linux 网卡驱动学习(一)(分析一个虚拟硬件的网络驱动例子)

Linux 网卡驱动学习(分析一个虚拟硬件的网络驱动例子),网络设备驱动层是连接网络堆栈协议层和网络硬件的中间层。...
  • xy010902100449
  • xy010902100449
  • 2015年07月29日 19:32
  • 3663

设备驱动的异步通知实现

主要实现思路是:设备I/O驱动如果准备好数据(用户可读或者可写),向用户进程发送信号,用户进程收到信号后调用相关的信号处理函数对设备的数据进行访问。 实现上述的思路需要两方面的实现:用户程序的实现、...
  • vhghhd
  • vhghhd
  • 2011年10月10日 13:08
  • 394

设备驱动中异步通知编程

设备驱动中异步通知编程主要用到一项数据结构和两个函数。 1.数据结构------fasync_struct结构体。 2.两个函数    (1)处理FASYNC标志变更的 int fasync_...
  • buaa_shang
  • buaa_shang
  • 2013年06月15日 22:22
  • 1197

poll和异步通知的引入

在linux驱动学习的过程中,会遇到同步互斥、异步通信、poll机制、阻塞和非阻塞的这些概念,对于这些概念在什么时候引入来讲述比较好呢?下面只讲述poll机制和异步通信。 对于按键驱动,如何去返回按...
  • gaosong819
  • gaosong819
  • 2014年05月20日 19:41
  • 568

Linux内核开发之异步通知与异步I/O《来自linux设备开发详解》

阻塞I/O意味着一直等待设备可访问再访问,非阻塞I/O意味着使用poll()来查询是否可访问,而异步通知则意味着设备通知应用程序自身可访问。(异步通知:很简单,一旦设备准备好,就主动通知应用程序,这种...
  • wangpengqi
  • wangpengqi
  • 2012年10月15日 21:34
  • 1806
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Linux 设备驱动学习之 异步通知
举报原因:
原因补充:

(最多只允许输入30个字)