• 《中断学习 —— 内核中断触发用户中断(异步通知机制)》


    1.异步通知机制

      一旦设备就绪,则主动通知应用程序,这样应用程序根本就不需要查询设备状态,是一种“信号驱动的异步I/O”。信号是在软件层次上对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是异步的,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候会到达。

      从用户程序的角度考虑:为了启动文件的异步通知机制,用户程序必须执行两个步骤。首先,他们指定一个进程作为文件的“属主(owner)”。当进程使用fcntl系统调用执行F_SETOWN命令时,属主进程的进程ID号就被保存在filp->f_owner中。这一步是必需的,目的是为了让内核知道应该通知哪个进程。 然后为了真正启动异步通知机制,用户程序还必须在设备中设置FASYNC标志,这通过fcntl的F_SETFL命令完成的。 执行完这两个步骤之后,输入文件就可以在新数据到达时请求发送一个SIGIO信号。该信号被发送到存放在filp->f_owner中的进程(如果是负值就是进程组)。

      在用户程序中,为了捕获信号,可以使用signal()函数来设置对应信号的处理函数:

    void (*signal(int signum, void (*handler))(int)))(int);
    

      该函数原型较难理解, 它可以分解为:

    typedef void (*sighandler_t)(int);                        //消息处理函数
    sighandler_t signal(int signum, sighandler_t handler));   //连接信号与消息处理函数
    第一个参数:指定信号的值
    第二个参数:指定针对前面信号值的处理函数,若为SIG_IGN,表示忽略该信号;若为SIG_DFL,表示采用系统默认方式处理信号;若为用户自定义的函数,则信号被捕获到后,该函数将被执行。
    返回值:如果signal()调用成功,它返回最后一次为信号signum绑定的处理函数的handler值,失败则返回SIG_ERR。
    

      

    2.应用程序fcntl设置

    #include <stdio.h>
    #include <fcntl.h>
    #include <errno.h>
    #include <signal.h>
    #include <sys/types.h>
    #include <unistd.h>
    
    
    void signal_handler(int sig)
    {
    	if(sig == SIGIO)
    	{
    		printf("Receive io signal from kernel!
    ");
    	}
    }
    
    int main(int argc, char **argv)
    {
      int fd;
    
      fd = open("/dev/xxx", O_RDONLY);    
      
      signal(SIGIO, signal_handler);  
      fcntl(fd, F_SETOWN, getpid());
      int oflags = fcntl(fd, F_GETFL);
      fcntl(fd, F_SETFL, oflags | FASYNC);
      while(1)
      {
        sleep(100);   
      }
            
      return 0;  
    }
    

      

    fcntl函数原型: 

      功能描述:根据文件描述词来操作文件的特性。针对(文件)描述符提供控制,参数fd是被参数cmd操作(如下面的描述)的描述符。

    #include <unistd.h>
    #include <fcntl.h>
    
    int fcntl(int fd, int cmd);
    int fcntl(int fd, int cmd, long arg);         
    

    fcntl函数有5种功能:

    1. 复制一个现有的描述符(cmd=F_DUPFD或 F_DUPFD_CLOEXEC)
    2. 获得/设置文件描述符标记(cmd=F_GETFD或F_SETFD)
    3. 获得/设置文件状态标记(cmd=F_GETFL或F_SETFL)
    4. 获得/设置异步I/O所有权(cmd=F_GETOWN或F_SETOWN)
    5. 获得/设置记录锁(cmd=F_GETLK,F_SETLK或F_SETLKW)

    针对上面所使用的几个cmd做个详细说明:

    F_SETOWN:设置接收SIGIO和SIGURG信号的进程ID或进程组ID。

    F_GETFL:对应于fd的文件状态标志作为函数值返回。

    F_SETFL:将文件状态标志设置为第3个参数的值。

    fcntl(STDIN_FILENO, F_SETOWN, getpid()); //设置本进程为STDIN_FILENO文件的拥有者,没有这一步,内核不会知道应该将信号发给哪个进程
    oflags = fcntl(STDIN_FILENO, F_GETFL);   //获取设备文件的f_flags
    fcntl(STDIN_FILENO, F_SETFL, oflags | FASYNC); //为了启用异步通知机制,还需对设备设置FASYNC标志
    

    从驱动程序角度考虑: 

      应用程序在执行F_SETFL启用FASYNC时,调用驱动程序的fasync方法。只要filp->f_flags中的FASYNC标识发生了变化,就会调用该方法,以便把这个变化通知驱动程序,使其能正确响应。文件打开时,FASYNC标志被默认为是清除的。当数据到达时,所有注册为异步通知的进程都会被发送一个SIGIO信号。

    Linux的这种通用方法基于一个数据结构和两个函数:

    extern int fasync_helper(int, struct file *, int, struct fasync_struct **);
    //当一个打开的文件的FASYNC标志被修改时,调用驱动程序的fasync方法间接调用fasync_helper函数以便将当前进程加入到驱动程序的异步通知等待队列中。
    extern void kill_fasync(struct fasync_struct **, int, int);
    //当设备可访问时,可使用kill_fasync函数发信号所有的相关进程。进程进而调用绑定的消息处理函数。

    总结:应用程序使用fcntl()设置当前进程的pid和FASYNC标志。进而调用驱动程序的fasync(),即fasync_helper()。然后申请和设置fasync_struct结构,将此结构挂载到驱动程序的fasync_struct结构链表中。当设备可用时,驱动程序会使用kill_fasync(),从fasync_struct链表中,查找所有的等待进程,然后调用send_sigio发送相应的消息给进程。进程接收到消息,就会跳转到与消息绑定的消息处理函数中。

     https://blog.csdn.net/lzs940320/article/details/108558785    

    3.实例编写

    3.1实例编写思路

      

    // 1,设置信号处理方法
    signal(SIGIO,catch_signale);
    
    // 2,将当前进程设置成SIGIO的属主进程
    fcntl(fd, F_SETOWN, getpid());
    
    // 3,将io模式设置成异步模式
    int flags  = fcntl(fd, F_GETFL);
    fcntl(fd, F_SETFL, flags | FASYNC );
    
    驱动--发送信号
    1,需要和进程进行关联--记录信号该发送给谁
    
    实现一个fasync的接口
    
    static struct fasync_struct *async; //声明fasync_struct
    
    int key_drv_fasync(int fd, struct file *filp, int on)
    {
        //只需要调用一个函数记录信号该发送给谁
        return fasync_helper(fd, filp, on,  &aysnc);
    }
    
    2,在某个特定的时候去发送信号,在有数据的时候
        //发送信号
        kill_fasync(&aysnc, SIGIO, POLL_IN);
    

      

      

      

      

      

  • 相关阅读:
    话说Hibernate和ADO.NET —练习随笔小记
    二次开发WinWebMail邮件系统接口 企业邮件服务器解决方案
    一个Windows后台服务(.Net的C#版) 定时访问数据库循环发送手机短信
    SQL UPDATE 联合表更新的问题
    2009新的篇章,惠海→时代财富→广佛都市网
    在WebService中使用Session或Cookie实现WebService身份验证(客户端是Flex)
    门户网站的形成—CMS内容管理系统
    CSS实现0.5px的边框或线
    《后人诗》
    CentOS6下docker的安装和使用
  • 原文地址:https://www.cnblogs.com/zhuangquan/p/13818778.html
Copyright © 2020-2023  润新知