zoukankan      html  css  js  c++  java
  • Smart20学习记录----异步通知

    异步通知:

    阻塞与非阻塞访问、poll()函数提供了较好地解决设备访问的机制(应用程序主动访问)

    异步通知:一旦设备就绪,则主动通知应用程序,这样应用程序根本就不需要查询设备状态,这一点非常类似于硬件上“中断”的概念,比较准确的称谓是“信号驱动的异步 I/O”

    阻塞 I/O 意味着一直等待设备可访问后再访问,非阻塞 I/O 中使用 poll()意味着查询设备是否可访问,而异步通知则意味着设备通知自身可访问,实现了异步 I/O。由此可见,这几种方式 I/O可以互为补充。

    阻塞、非阻塞 I/O、异步通知本身没有优劣,应该根据不同的应用场景合理选择。

    linux异步通知编程:

    1.信号的接受:

    在用户程序中,为了捕获信号,可以使用 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。   

    使用信号实现异步通知的应用程序实例:

    1  #include <sys/types.h>
    2  #include <sys/stat.h> 
    3  #include <stdio.h> 
    4  #include <fcntl.h> 
    5  #include <signal.h> 
    6  #include <unistd.h> 
    7  #define MAX_LEN 100 
    8  void input_handler(int num) 
    9  { 
    10   char data[MAX_LEN]; 
    11   int len; 
    12  
    13   /* 读取并输出 STDIN_FILENO 上的输入 */ 
    14   len = read(STDIN_FILENO, &data, MAX_LEN); 
    15   data[len] = 0; 
    16   printf("input available:%s
    ", data); 
    17 } 
    18  
    19 main() 
    20 { 
    21   int oflags; 
    22  
    23   /* 启动信号驱动机制 */ 
    24   signal(SIGIO, input_handler); 
    25   fcntl(STDIN_FILENO, F_SETOWN, getpid()); 
    26   oflags = fcntl(STDIN_FILENO, F_GETFL); 
    27   fcntl(STDIN_FILENO, F_SETFL, oflags | FASYNC); 
    28  
    29   /* 最后进入一个死循环,仅为保持进程不终止,如果程序中 
    30   没有这个死循会立即执行完毕 */ 
    31   while (1); 
    32 } 
    

      

    上述代码 24 行为 SIGIO 信号安装 input_handler()作为处理函数,第 25 行设置本进程为 STDIN_FILENO 文件的拥有者(owner),没有这一步内核不会知道应该将信号发给哪个进程。而 为了启用异步通知机制,还需对设备设置 FASYNC 标志,26~27 行代码实现此目的。

    由此可见,为了在用户空间中能处理一个设备释放的信号,它必须完成 3 项工作。 (1)通过 F_SETOWN IO 控制命令设置设备文件的拥有者为本进程,这样从设备驱动发出的 信号才能被本进程接收到。 (2)通过 F_SETFL IO 控制命令设置设备文件支持 FASYNC,即异步通知模式。 (3)通过 signal()函数连接信号和信号处理函数。

    相关链接:http://blog.csdn.net/bailyzheng/article/details/7463775

    信号的释放:

    在设备驱动和应用程序的异步通知交互中,仅仅在应用程序端捕获信号是不够的,因为信号 没有的源头在设备驱动端。因此,应该在合适的时机让设备驱动释放信号,在设备驱动程序中增 加信号释放的相关代码。 为了使设备支持异步通知机制,驱动程序中涉及 3 项工作。

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

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

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

    设备驱动中异步通知编程比较简单,主要用到一项数据结构和两个函数。数据结构是
    fasync_struct 结构体,两个函数分别是:
    处理 FASYNC 标志变更的。
    int fasync_helper(int fd, struct file *filp, int mode, struct fasync_struct **fa);
    释放信号用的函数。
    void kill_fasync(struct fasync_struct **fa, int sig, int band);

    相关驱动:

    #include <linux/module.h>//support module load and unload
    #include <linux/types.h>//special type definition,like dev_t off_t defined by typedef
    #include <linux/fs.h>//struct file_operations
    #include <linux/errno.h>//return value 
    #include <linux/mm.h>//memory mannage ,include kmalloc.kfree and so on
    #include <linux/sched.h>//process schedule 
    #include <linux/init.h>//
    #include <linux/cdev.h>//char device structure definition
    #include <linux/io.h>//io operation function ,like ioremap,iowrite
    #include <linux/poll.h>
    
    
    MODULE_AUTHOR("qigaohua") ;
    MODULE_LICENSE("Dual BSD/GPL") ;
    
    #define GLOBALFIFO_SIZE 0x1000 //1M
    #define CLEAR_ALL 0x01 //clear fifo 
    #define GLOBALFIFO_MAJOR 0//major device No
    
    static int globalfifo_major = GLOBALFIFO_MAJOR  ;
    /*define globalfifo structure*/
    struct globalfifo_dev {
        struct cdev dev ;
        unsigned int current_len ;
        unsigned char mem[GLOBALFIFO_SIZE] ;
        struct semaphore sem ;
        wait_queue_head_t r_wait ;
        wait_queue_head_t w_wait ;
        struct fasync_struct fasync_queue;
    } ;
    struct globalfifo_dev *globalfifo_devp = NULL;
    
    static int globalfifo_open(struct inode *inode, struct file *filp)
    {
        filp->private_data = globalfifo_devp ;
        return 0 ;
    }
    static int globalfifo_release(struct inode *inode ,struct file *filp)
    {
        globalfifo_fasync(-1, filp, 0);
        return 0 ;
    }
    
    static int globalfifo_ioctl(struct inode *inode, struct file *filp,
        int cmd, unsigned long arg)
    {
        struct globalfifo_dev *dev = filp->private_data ;
        switch (cmd)
        {
            case CLEAR_ALL:
                /*note:down_intterruptible is more suitable,why?*/
                down_interruptible(&dev->sem) ;
                dev->current_len = 0 ;
                memset(dev->mem,0,GLOBALFIFO_SIZE) ;
                up(&dev->sem) ;
                printk(KERN_INFO "clear globalfifo successfully!
    ") ;
                break ;
            default:
                printk(KERN_INFO "invalid command!
    ") ;
                return -EINVAL ;
        }
    }
    
    static unsigned int globalfifo_poll(struct file *filp, poll_table *wait)
    {
        unsigned int mask = 0 ;
        struct globalfifo_dev *dev = filp->private_data ;
        
        down_interruptible(&dev->sem) ;
        
        poll_wait(filp,&dev->r_wait,wait) ;
        poll_wait(filp,&dev->w_wait,wait) ;
    
        if(dev->current_len !=0 )
        {
            mask |= POLLIN | POLLRDNORM ;
        }
        if(dev->current_len< GLOBALFIFO_SIZE )
        {
            mask |= POLLOUT | POLLWRNORM ;
        }
        up(&dev->sem) ;
        return mask ;
    }
    static ssize_t globalfifo_read(struct file *filp, char __user *buf,
        size_t count, loff_t *ppos)
    {
        int ret ;
        struct globalfifo_dev *dev = filp->private_data ;
        DECLARE_WAITQUEUE(wait,current) ;
        down_interruptible(&dev->sem) ;
        add_wait_queue(&dev->r_wait,&wait) ;
        if(dev->current_len == 0 )
        {
            if( filp->f_flags & O_NONBLOCK)
            {
                ret = -EAGAIN ;
                goto out ;
            }
            __set_current_state(TASK_INTERRUPTIBLE) ;
            up(&dev->sem) ;
            schedule() ;
            if(signal_pending(current))
            {
                ret = - ERESTARTSYS ;
                goto out2 ;
            }
            down_interruptible(&dev->sem) ;
        }
        if(count>dev->current_len)
        {
            count = dev->current_len ;
        }
        if(copy_to_user(buf,dev->mem,count))
        {
            ret = -EFAULT ;
            goto out ;
        }else
        {
            memcpy(dev->mem,dev->mem+count,dev->current_len-count) ;
            dev->current_len -= count ;
            printk(KERN_INFO "read %d bytes(s),current_len:%d
    ",
                count,dev->current_len) ;
            wake_up_interruptible(&dev->w_wait) ;
            ret = count  ;
        }
    out:
        up(&dev->sem) ;
    out2:
        remove_wait_queue(&dev->w_wait,&wait) ;
        set_current_state(TASK_RUNNING) ;
        return ret ;
    }
    
    static int globalfifo_fasync(int fd, struct file *filp, int mode) 
    {
        struct globalfifo_dev *dev = file->private_data;
        return fasync_helper(fd, filp, mode, &dev->fasync_queue);
    }
    
    
    static ssize_t globalfifo_write(struct file *filp, const char __user *buf,
        size_t count, loff_t *ppos)
    {
        struct globalfifo_dev *dev = filp->private_data ;
        int ret = -1 ;
        DECLARE_WAITQUEUE(wait,current) ;
        
        down_interruptible(&dev->sem) ;
        add_wait_queue(&dev->w_wait,&wait) ;
        
        if(dev->current_len == GLOBALFIFO_SIZE)
        {
            if(filp->f_flags & O_NONBLOCK)
            {
                ret = -EAGAIN ;
                goto out ;
            }
            __set_current_state(TASK_INTERRUPTIBLE) ;
            up(&dev->sem) ;
            
            schedule() ;
            if(signal_pending(current))
            {
                ret = -ERESTARTSYS ;
                goto out2 ;
            }
            down_interruptible(&dev->sem) ;
        }
    
        if(count>GLOBALFIFO_SIZE-dev->current_len)
        {
            count = GLOBALFIFO_SIZE - dev->current_len ;        
        }
        if(copy_from_user(dev->mem+dev->current_len,buf,count))
        {
            ret = - EFAULT ;
            goto out ;
        }else
        {
            dev->current_len += count ;
            printk(KERN_INFO "written %d bytes(s),cuurent_len:%d",
                count , dev->current_len ) ;
            wake_up_interruptible(&dev->r_wait) ;
            if(dev->fasync_queue)
                kill_fasync(&dev->fasync_queue, SIGIO, POLLIN);
            ret = count ;
        } 
    out:
        up(&dev->sem) ;
    out2:
        remove_wait_queue(&dev->w_wait,&wait) ;
        set_current_state(TASK_RUNNING) ;
        return ret ;
    }
    static const struct file_operations globalfifo_fops = {
        .owner   = THIS_MODULE ,
        .open    = globalfifo_open ,
        .read    = globalfifo_read ,
        .write   = globalfifo_write ,
        .ioctl   = globalfifo_ioctl ,
        .poll    = globalfifo_poll ,
        .fasync  = globalfifo_fasync,
        .release = globalfifo_release ,
    } ;
    int __init globalfifo_init(void)
    {
        int ret ;
        dev_t devno = MKDEV(globalfifo_major, 0 ) ;
        if(globalfifo_major)
        {
            ret = register_chrdev_region(devno,1,"globalfifo") ;
        }else
        {
            ret = alloc_chrdev_region(&devno,0,1,"globalfifo") ;
            globalfifo_major = MAJOR(devno) ;
        }
        if(ret<0){
            printk(KERN_INFO "apply for device No failed! major:%d,minor:%d
    ",MAJOR(devno),MINOR(devno)) ;
            return ret ;
        }
        globalfifo_devp = kmalloc(sizeof(struct globalfifo_dev),GFP_KERNEL) ;
        if(!globalfifo_devp){
            ret = - ENOMEM ;
            goto fail_malloc ;
        }
    
        memset(globalfifo_devp, 0, sizeof(struct globalfifo_dev)) ;
    
        cdev_init(&globalfifo_devp->dev, &globalfifo_fops) ;
    
        globalfifo_devp->dev.owner = THIS_MODULE ;
        globalfifo_devp->dev.ops = &globalfifo_fops ;
        if(cdev_add(&globalfifo_devp->dev, devno,1)) {
            printk(KERN_NOTICE "add cdev failed!
    ") ;
        }
    
        INIT_MUTEX(&globalfifo_devp->sem) ;
        init_waitqueue_head(&globalfifo_devp->r_wait) ;
        init_waitqueue_head(&globalfifo_devp->w_wait) ;
        printk(KERN_INFO "init globalfifo device successfully!
    " ) ;
        return 0 ;
    fail_malloc:
        unregister_chrdev_region(devno,1) ;
        return ret ;
    }
    void __exit globalfifo_exit(void)
    {
        if(globalfifo_devp != NULL ){
            cdev_del(&globalfifo_devp->dev) ;
            kfree(globalfifo_devp) ;
            unregister_chrdev_region(MKDEV(globalfifo_major,0),1) ;
        }
        printk(KERN_INFO "unload globalfifo device successfully!
    ") ;
    }
    //module_param(globalfifo_major, int , S_IRUGO) ;
    
    module_init(globalfifo_init) ;
    module_exit(globalfifo_exit) ;

    来自《Linux设备驱动开发详解》

  • 相关阅读:
    (OK) VirtualBox 5.0.10 中 Fedora 23 在安装了增强工具后无法自动调节虚拟机分辨率的问题
    (OK) install-fedora23—gnome classic—dnf—gdm—multi-user—graphical
    (OK) fedora23——add “opening terminal” in the menu of right click
    (OK) Install Docker on Fedora 23
    (OK) 运行cBPM in Fedora23
    错误:‘bOS::SQL::SQLCommand::SQLOperation’既不是类也不是命名空间
    My enum is not a class or namespace
    【t049】&&【u001】足球
    【u002】数列排序(seqsort)
    【u003】计算概率
  • 原文地址:https://www.cnblogs.com/qigaohua/p/5448428.html
Copyright © 2011-2022 走看看