• linux下使用异步通知


    阻塞式I/O是一直等待直到设备可以访问,非阻塞式I/O是定期轮询设备是否可以访问。

    异步通知则是当设备可以访问时才主动通知应用程序,有点像设备的硬中断。

    并不是所有的设备都支持异步通知,应用程序通常假设只有套接字和终端才有异步通知的能力。

    异步通知存在一个问题,当进程收到SIGIO信号时,它并不知道是哪个文件有了新的输入,如果

    有多于一个的文件可以异步通知同一个进程,那么应用进程还需要借助于poll或select来确定输入

    的来源。

    应用层

    fcntl - manipulate file descriptor

    fcntl(fd, F_SETFL, flags | O_ASYNC);

    效果:

    If you set the O_ASYNC status flag on a file descriptor by using the F_SETFL command of fcntl(),

    a SIGIO signal is sent whenever input or output becomes possible on that file descriptor.

    例子

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <signal.h>
    #include <fcntl.h>
    
    #define MAX_LEN 100
    
    void my_handler(int signum)
    {
        char data[MAX_LEN];
        int len;
    
        len = read(STDIN_FILENO, &data, MAX_LEN);
        data[len] = 0;
        
        printf("Input message: %s
    ", data);
        exit(0);
    }
    
    int main(void)
    {
        int oflags;
    
        /* set new SIGIO handler */
        signal(SIGIO, my_handler);
        /* set fd's owner process */
        fcntl(STDIN_FILENO, F_SETOWN, getpid());
        /* get old fd flags */
        oflags = fcntl(STDIN_FILENO, F_GETFL);
        /* set new fd flags */
        fcntl(STDIN_FILENO, F_SETFL, oflags | O_ASYNC);
    
        /* infinitely wait until recv SIGIO */
        while(1);
        return 0;
    }
    

    为了能处理设备发出的SIGIO信号,用户程序需要做:

    1. 设置设备文件的拥有者为本进程,这样一来才能收到设备驱动发出的SIGIO信号。

    2. 使设备文件支持异步通知,即设置O_ASYNC标志。

    3. 通过signal()指定SIGIO的处理函数。

    设备驱动

    struct fasync_struct {
        spinlock_t fa_lock;
        int magic;
        int fa_fd; /* 文件描述符 */
        struct fasync_struct *fa_next; /* 用于链入单向链表 */
        struct file *fa_file; /* fa_file->f_owner记录接收信号的进程 */
        struct rcu_head fa_rcu;
    };
    

    (1) 设备的异步通知链

    struct xxx_dev {
        struct cdev cdev;
        ...
        struct fasync_struct *async_queue; /* 异步通知链 */
    };
    

    (2) 插入到异步通知链

    static int xxx_fasync(int fd, struct file *filp, int on)
    {
        struct xxx_dev *dev = filp->private_data;
        return fasync_helper(fd, filp, on, &dev->async_queue);
    }
    

    (3) 发送信号通知进程

    设备使用kill_fasync()来发送信号给用户进程,一般sig为SIGIO,可读时band为POLL_IN,

    可写时band为POLL_OUT。

    static ssize_t xxx_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_ops)
    {
        struct xxx_dev *dev = filp->private_data;
        ...
        if (dev->async_queue) {
            kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
        }
        ...
    }
    

    (4) 从异步通知链中删除

    在关闭文件时,需要把对应的异步通知结构从链表中删除。

    static xxx_release(struct inode *inode, struct file *filp)
    {
        struct xxx_dev *dev = filp->private_data;
        ...
        fasync_helper(-1, filp, 0, &dev->async_queue);
        ...
    }
    

    内核API

    异步通知结构体的插入和删除,失败返回负值,没有变化返回0,有变化返回正值。

    /* fasync_helper() is used by almost all character device drivers to set up the 
     * fasync queue, and for regular files by the file lease code. It returns negative
     * on error, 0 if it did no changes and positive if it added/deleted the entry.
     */
    
    int fasync_helper(int fd, struct file *filp, int on, struct fasync_struct **fapp)
    {
        if (!on) /* 删除 */
            return fasync_remove_entry(filp, fapp);
        return fasync_add_entry(fd, filp, fapp); /* 插入 */
    }
    
    


    发送信号,通知用户进程,sig一般设为SIGIO,可读时band为POLL_IN,可写时band为POLL_OUT。

    void kill_fasync(struct fasync_struct **fp, int sig, int band)
    {
        /* First a quick test without locking: usually the list is empty. */
        if (*fp) {
            rcu_read_lock();
            kill_fasync_rcu(rcu_dereference(*fp), sig, band);
            rcu_read_unlock();
        }
    }
    

    Reference

    [1]. http://www.cnblogs.com/hanyan225/archive/2010/10/20/1857040.html

    [2]. 《设备驱动程序》

  • 相关阅读:
    SVG:中国地图
    网页编程工具:EditPlus
    js插件
    html: 仿制soundmanager2右上角面板
    代码:页面布局(含图片列表布局)
    写着玩: 图片 圆盘
    表格
    按钮
    插件:左侧下拉菜单
    颜色
  • 原文地址:https://www.cnblogs.com/aiwz/p/6333245.html
Copyright © 2020-2023  润新知