字符设备驱动第八课----异步通知(信号驱动IO)

来源:互联网 发布:java分布式系统架构 编辑:程序博客网 时间:2024/05/10 05:11

概述

类比运用程序中的kill-----signal,在运用程序中常常一个进程用kill(pid,sig)向另一进程发信号,另一个进程用signal(sig,handler)绑定相应的处理函数,实现了异步通知。今天要讲的就是:运用程序要读,但它并不知道啥时候有东西可读,用read()阻塞去读显然效率不高,read()配合IO多路复用非阻塞一直在那里轮询的话效率也不好。这里采用的办法是:驱动层有数据可读的时候kill一个SIGIO信号给运用层,运用层收到SIGIO信号后调用预先绑定好的处理函数把数据读走。

若还迷糊概念,请看看这位大神的清晰讲解:
信号驱动IO与异步通知

<include/linux/fs.h>struct file_operations {    int (*open) (struct inode *, struct file *);    int (*flush) (struct file *, fl_owner_t id);    int (*release) (struct inode *, struct file *);    int (*fsync) (struct file *, loff_t, loff_t, int datasync);       //用于异步通知    ...}
<include/linux/fs.h>struct fasync_struct {    spinlock_t      fa_lock;    int         magic;    int         fa_fd;    struct fasync_struct    *fa_next; /* singly linked list */    struct file     *fa_file;    struct rcu_head     fa_rcu;};
/** 功能: 得到异步通知结构。根据mod,将异步通知结构体加入链表* 运用程序端调用这些函数改变标志和owner时就调用了这个底层实现。*    fcntl(STDIN_FILENO,F_SETOWN,getpid());*    oflags = fcntl(STDIN_FILENO,F_GETFL);*    fctcl(STDIN_FILENO,F_SETFL,oflags | FASYNC);* 输入参数:  fd:             文件描述符*          filp:          file结构体指针* 输出参数:fapp:得到的异步通知结构体          */int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)
/* * 功能:通过异步通知结构发信号,此信号发出时运用端就会收到SGIO信号,就会回调预先绑定的处理函数。 * 参数:struct fasync_struct **fp:  异步通知结构 *      int signo:                  信号(SIGIO) *      int events:                 事件:POLLIN、POLLOUT */void kill_fasync(struct fasync_struct **fp, int sig, int band)

范例:

1.驱动端:

#include <linux/init.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/cdev.h>#include <linux/fs.h>#include <linux/errno.h>#include <asm/current.h>#include <linux/sched.h>#include <linux/uaccess.h>#include <linux/poll.h>#include <asm/atomic.h>#include <linux/mutex.h>#include <linux/wait.h>#include <linux/device.h>static struct class *cls = NULL;static int major = 0;static int minor = 0;const  int count = 6;#define DEVNAME "demo"static struct cdev *demop = NULL;static atomic_t tv;static wait_queue_head_t wq;static struct fasync_struct *fasync = NULL;//定义异步通知结构体#define KMAX 1024static char kbuf[KMAX];static int counter = 0;//打开设备static int demo_open(struct inode *inode, struct file *filp){    //get major and minor from inode    printk(KERN_INFO "(major=%d, minor=%d), %s : %s : %d\n",        imajor(inode), iminor(inode), __FILE__, __func__, __LINE__);    if(!atomic_dec_and_test(&tv)){        atomic_inc(&tv);        return -EBUSY;    }    memset(kbuf, 0, KMAX);    counter = 0;    return 0;}//关闭设备static int demo_release(struct inode *inode, struct file *filp){    //get major and minor from inode    printk(KERN_INFO "(major=%d, minor=%d), %s : %s : %d\n",        imajor(inode), iminor(inode), __FILE__, __func__, __LINE__);    atomic_inc(&tv);    return 0;}//读设备//ssize_t read(int fd, void *buf, size_t count)static ssize_t demo_read(struct file *filp, char __user *buf, size_t size, loff_t *offset){    int err = 0;    struct inode *inode = filp->f_path.dentry->d_inode;  //获取文件的inod号    //get major and minor from inode    printk(KERN_INFO "(major=%d, minor=%d), %s : %s : %d\n",        imajor(inode), iminor(inode), __FILE__, __func__, __LINE__);    if(!counter){        if(filp->f_flags & O_NONBLOCK){            return -EAGAIN;        }        err = wait_event_interruptible(wq, (0 != counter));//睡在条件上的等待队列        if(err){                                            //没东西可读,睡            return err;        }    }    if(counter < size){        size = counter;    }    if(copy_to_user(buf, kbuf, size)){        return -EAGAIN;    }    counter = 0;    return size;}//写设备static ssize_t demo_write(struct file *filp, const char __user *buf, size_t size, loff_t *offset){    struct inode *inode = filp->f_path.dentry->d_inode;    //get major and minor from inode    printk(KERN_INFO "(major=%d, minor=%d), %s : %s : %d\n",        imajor(inode), iminor(inode), __FILE__, __func__, __LINE__);    if(size > KMAX){        return -ENOMEM;    }    if(copy_from_user(kbuf, buf, size)){        return -EAGAIN;    }    counter = size;    wake_up_interruptible(&wq);//广播唤醒等待队列    kill_fasync(&fasync, SIGIO, POLLIN);//向fasync结构体发信号,    //与fasync关联的进程(通过fcntl(...,pid)系列函数关联)就会收到SIGIO信号    return size;}/* IO多路复用支持*/static unsigned int demo_poll(struct file *filp, struct poll_table_struct *pts){    unsigned int mask = 0;    struct inode *inode = filp->f_path.dentry->d_inode;    //get major and minor from inode    printk(KERN_INFO "(major=%d, minor=%d), %s : %s : %d\n",        imajor(inode), iminor(inode), __FILE__, __func__, __LINE__);    poll_wait(filp, &wq, pts);//io多路复用支持,只有等待队列中有就绪的事件才会往下走,否则阻塞    if(counter){        mask = (POLLIN | POLLRDNORM);//返回,告诉运用层的poll函数:就绪事件是输入事件    }    return mask;}/*异步通知接口函数,应用层调fcntl()时调到此函数*/static int demo_fasync(int fd, struct file *filp, int mode){    struct inode *inode = filp->f_path.dentry->d_inode;    //get major and minor from inode    printk(KERN_INFO "(major=%d, minor=%d), %s : %s : %d\n",        imajor(inode), iminor(inode), __FILE__, __func__, __LINE__);    return fasync_helper(fd, filp, mode, &fasync);//根据mod,将异步通知结构体加入链表                                                //或者从链表中移除。得到信息并填充到fasync结构体中}static struct file_operations fops = {    .owner  = THIS_MODULE,    .open   = demo_open,    .release= demo_release,    .read   = demo_read,    .write  = demo_write,    .poll   = demo_poll,    .fasync = demo_fasync,};static int __init demo_init(void){    dev_t devnum;    int ret, i;    struct device *devp = NULL;    //get command and pid    printk(KERN_INFO "(%s:pid=%d), %s : %s : %d\n",        current->comm, current->pid, __FILE__, __func__, __LINE__);    //1. alloc cdev obj    demop = cdev_alloc();    if(NULL == demop){        return -ENOMEM;    }    //2. init cdev obj    cdev_init(demop, &fops);    ret = alloc_chrdev_region(&devnum, minor, count, DEVNAME);    if(ret){        goto ERR_STEP;    }    major = MAJOR(devnum);    //3. register cdev obj    ret = cdev_add(demop, devnum, count);    if(ret){        goto ERR_STEP1;    }    cls = class_create(THIS_MODULE, DEVNAME);    if(IS_ERR(cls)){        ret = PTR_ERR(cls);        goto ERR_STEP1;    }    for(i = minor; i < (count+minor); i++){        devp = device_create(cls, NULL, MKDEV(major, i), NULL, "%s%d", DEVNAME, i);        if(IS_ERR(devp)){            ret = PTR_ERR(devp);            goto ERR_STEP2;        }    }    // init atomic_t    atomic_set(&tv, 1);    init_waitqueue_head(&wq);//初始化等待队列    //get command and pid    printk(KERN_INFO "(%s:pid=%d), %s : %s : %d - ok.\n",        current->comm, current->pid, __FILE__, __func__, __LINE__);    return 0;ERR_STEP2:    for(--i; i >= minor; i--){        device_destroy(cls, MKDEV(major, i));    }    class_destroy(cls);ERR_STEP1:    unregister_chrdev_region(devnum, count);ERR_STEP:    cdev_del(demop);    //get command and pid    printk(KERN_INFO "(%s:pid=%d), %s : %s : %d - fail.\n",        current->comm, current->pid, __FILE__, __func__, __LINE__);    return ret;}static void __exit demo_exit(void){    int i;    //get command and pid    printk(KERN_INFO "(%s:pid=%d), %s : %s : %d - leave.\n",        current->comm, current->pid, __FILE__, __func__, __LINE__);    for(i=minor; i < (count+minor); i++){        device_destroy(cls, MKDEV(major, i));    }    class_destroy(cls);    unregister_chrdev_region(MKDEV(major, minor), count);    cdev_del(demop);}module_init(demo_init);module_exit(demo_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("Farsight");MODULE_DESCRIPTION("Demo for kernel module");

2.运用程序端:

#include <stdio.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <poll.h>#include <signal.h>#include <errno.h>int fd = -1;void handler(int sig)//信号处理函数,与signal绑定的{    struct pollfd pfd = {        .fd = fd,        .events = POLLIN,    };    int ret = poll(&pfd, 1, ~0);//监控pfd,最大文件描述符1,永不超时    if(0 >= ret){               //没一个就绪则阻塞,只要其中有任意一个就绪就往下走        perror("poll");        return;    }#define MAX 1024    char buf[MAX];    memset(buf, 0, MAX);     if(0 > read(fd, buf, MAX)){        perror("read");    }else{        printf("RD: %s\n", buf);    }}int main(int num, char *argv[]){    if(2 != num){        printf("Usage: %s /dev/devfile\n", argv[0]);        return -1;    }    fd = open(argv[1], O_RDWR|O_NONBLOCK);    if(0 > fd){        printf("pid = %d, %s\n", getpid(), (char *)strerror(errno));        return -1;    }    signal(SIGIO, handler);//绑定信号处理函数    fcntl(fd, F_SETOWN, getpid());//关联收发,设置对应文件的拥有者是本进程,这样接下来才能进行信号的收发    int flag = fcntl(fd, F_GETFL);//读取对应文件描述符上的flg信息    flag  |= O_ASYNC;    fcntl(fd, F_SETFL, flag);     //设置对应文件描述符上的flg信息,使其支持异步通知                                  //这个函数实质上最终调用的是操作方法集中的.fasync标准接口,对应到驱动层中的相应函数    while(1){        printf("---------w: 1----------\n");    #define MAX 1024        char buf[MAX];        fgets(buf, MAX, stdin);                 write(fd, buf, strlen(buf));    }    close(fd);    return 0;}
0 0
原创粉丝点击