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设备驱动开发详解》

原文地址:https://www.cnblogs.com/qigaohua/p/5448428.html