zoukankan      html  css  js  c++  java
  • 字符设备驱动第八课----异步通知(信号驱动IO)

    概述

    类比运用程序中的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 1024
    static 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
    ",
            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
    ",
            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
    ",
            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
    ",
            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
    ",
            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
    ",
            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
    ",
            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.
    ",
            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.
    ",
            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.
    ",
            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
    ", buf);
        }
    }
    
    int main(int num, char *argv[])
    {
        if(2 != num){
            printf("Usage: %s /dev/devfile
    ", argv[0]);
            return -1;
        }
    
        fd = open(argv[1], O_RDWR|O_NONBLOCK);
        if(0 > fd){
            printf("pid = %d, %s
    ", 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----------
    ");
        #define MAX 1024
            char buf[MAX];
            fgets(buf, MAX, stdin);         
            write(fd, buf, strlen(buf));
        }
    
        close(fd);
    
        return 0;
    }
    
  • 相关阅读:
    DDoS攻击
    CSRF攻击
    正向代理和反向代理
    DNS协议
    四次挥手
    Nginx重要概念之lingering_close
    Nginx重要概念之pipeline
    Nginx重要概念之keepalive
    http1.0、http1.1、http2.0三者的区别
    Vue axios封装二
  • 原文地址:https://www.cnblogs.com/xxg1992/p/6636399.html
Copyright © 2011-2022 走看看