zoukankan      html  css  js  c++  java
  • Linux设备驱动中的异步通知与异步I/O

    异步通知概念: 异步通知的意识是,一旦设备就绪,则主动通知应用程序,这样应用程序根本就不需要查询设备状态,这一点非常类似于硬件上的“中断”概念,比较准确的称谓是“信号驱动的异步IO”,信号是在软件层次上对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求是一样的。信号是异步的,一个进进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。

    异步通知与设备访问: 阻塞IO意味着一直等待设备可访问后访问;非阻塞IO使用poll意味着查询设备是否可以访问;而异步通知则意味着设备通知自身可访问,实现了异步I/O

     用kill -l 命令可以查看Linux可用的信号
    信号的接收
    signal()函数来设置对应信号的处理函数。
    void (*signal(int signum,void(*handler))(int)))(int);
    //第一个参数指定信号的值;第二个参数指定针对前面信号值的处理函数,若位SIN_IGN,表示忽略该信号;若位SIG_DFL,表示采用系统默认方式处理信号;若位用户自定义
    函数,则信号被捕获到后,该函数将被执行
    //该函数原型比较难理解,可以分解为:typedef void(*sighandler_t)(int); sighandler_t signal(int signum,sighandler_t handler));
    如果signal调用成功,它返回最后一次信号signum绑定的处理函数handler值,失败则返回SIG_ERR.
    sigaction()函数可用于改变进程接收到特定信号后的行为。
    int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact));
    //该函数的第一个参数是信号的值,可以为除SIGKILL及SIGSTOP外的任何一个特定有效的信号;第二个参数是指向结构体sigaction的一个实例的指针,在sigaction的实例中
    指定了对特定信号的处理函数,若为空,则进程会以缺省方式对信号处理;第三个参数oldact指向的对象用来保存原来对相应信号的处理函数,可指定oldact为NULL.
    如果把第二和第三个参数都设为NULL,那么该函数用来检测信号的有效性。
    使用信号实现异步通知的应用程序实例:
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <stdio.h>
    #include <fcntl.h>
    #include <signal.h>
    #include <unistd.h>
    #define MAX_LEN 100
    void input handler(int num)
    {
    char data[MAX_LEN];
    int len;
    len=read(STDIN_FILENO,&data,MAX_LEN); //读取并输出STDIN_FILENO上的输入。
    data[len]=0;
    printf("input available:%s ",data);
    }
    main()
    {
    int oflags;
    //启动信号驱动机制
    signal(SIGIO,input_handler); //为SIGIO信号安装input_handler()作为处理函数
    fcntl(STDIN_FILENO,F_SETOWN,getpid());//这里的getpid()是获得当前进程,注意,没有这一步,内核不知道应该将信号发给哪个进程。
    在 Linux 中,实现文件上锁的函数有lock和fcntl,其中flock用于对文件施加建议性锁,而fcntl不仅可以施加建议性锁,还可以施加强制锁。同时,fcntl还能对文件的某一记录进行上锁,也就是记录锁。
    记录锁又可分为读取锁写入锁,其中读取锁又称为共享锁,它能够使多个进程都能在文件的同一部分建立读取锁。而写入锁又称为排斥锁,在任何时刻只能有一个进程在文件的某个部分上建立写入锁。当然,在文件的同一部分不能同时建立读取锁和写入锁。
    注意:
    fcntl是一个非常通用的函数,它还可以改变文件进程各方面的属性,在本节中,主要介绍它建立记录锁的方法,关于它其他用户感兴趣的读者可以参看fcntl手册。

           oflags=fcntl(STDIN_FILENO,F_GETFL); //为了启动一步通知机制,还需对设备设置FASYNC标志

           fcntl(STDIN_FILENO,F_SETFL,oflags|FASYNC);

           //最后进入一个死循环,仅为保持进程不终止,如果程序中没有死循环会立即执行完毕

           while(1);

          }

     信号释放:

              在设备驱动程序与应用程序的异步通知交互中,应用程序捕获信号,驱动程序释放信号。

        为了使设备支持异步通知机制,驱动程序中涉及3项工作。

        (1)支持F_SETOWN命令,能在这个控制命令处理中设置filp->f_owner为对应进程ID。不过此项工作已由内核完成,设备驱动无需处理。

        (2)支持F_SETFL命令的处理,每当FASYNC标志改变时,驱动程序中的fasync()函数将得以执行。因此,驱动中应该实现fasync()函数。

        (3)在设备资源可获得时,调用kill_fasync()函数激发相应的信号。

             异步通知处理过程中用户空间和设备驱动的交互。

        

        异步通知变成主要用到一项数据结构和两个函数。数据结构是fasync_struct结构体,两个函数是fasync_helper,kill_fasync。

           int fasync_helper(int fd,struct file *filp,int mode,struct fasync_struct **fa);  //处理FASYNC标志变更

          void kill_fasync(struct fasync_struct **fa,int sig,int band);  //释放信号

          支持异步通知的设备结构体模板

                                  struct xxx_dev{

                                        struct cdev cdev;  //cdev结构体

                                        ...

                                        struct fasync_struct *async_queue;          //异步结构体指针

                                       };

          支持异步通知的设备驱动fasync()函数模板

                                  static int xxx_fasync(int fd,struct file *filp,int mode)

                                       {

                                          struct xxx_dev *dev=filp->private_data;

                                          return fasync_helper(fd,filp,mode,&dev->async_queue);

                                       }

          在设备资源可以获得时,应该调用kill_fasync()释放SIGIO信号,可读时第三个参数设置为POLL_IN,可写时第三个参数设置为POLL_OUT.

          支持异步通知的设备驱动信号释放范例:

                                        static ssize_t xxx_write(struct file *filp,const char_user *buf,size_t count,loff_t *f_pos)

                                                {

                                                   struct xx_dev *dev=filp->private_data;

                                                   ...

                                                  //产生异步读信号

                                                   if(dev->async_queue)

                                                      kill_fasync(&dev->async_queue,SIGIO,POLL_IN);

                                                       ...

                                             }

     最后在关闭文件时候,即在release函数中,应该将文件从异步通知的列表中删除。

              static int xxx_release(struct inode *inode,struct file *filp)

          {

               //将文件从异步通知列表中删除

                xxx_fasync(-1,filp,0);

                 ...

                 return 0;

            }


                  

      

  • 相关阅读:
    smarty基础
    phpcms 内容模块PC标签调用
    phpcms v9中的$CATEGORYS栏目数组
    PHP如何实现验证码
    PHP生成一个不重复随机数组的封装方法
    简单实现php文件管理
    PHP-----作业:查询数据,在页面上显示
    PHP-----设计模式六大原则
    PHP-----静态
    PHP-----面向对象的设计模式:工厂模式例题
  • 原文地址:https://www.cnblogs.com/God-boy1/p/3670983.html
Copyright © 2011-2022 走看看