• Linux 驱动层实现阻塞和非阻塞


    linux应用层的函数默认是阻塞型的,但是要想真正实现阻塞,还需要驱动的支持才行。

    例:open()、scanf()、fgets()、read()、accept() 等

    1、默认情形,驱动层不实现阻塞和非阻塞

    struct samsung_key{
        int major;
        int irqno;
        struct class *cls;
        struct device *dev;
        struct key_event event;
    };
    struct samsung_key *key_dev;
    
    ssize_t key_drv_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos)
    {
        int ret;
    
        ret = copy_to_user(buf, &key_dev->event, count);
        if(ret > 0)
        {
            printk("copy_to_user error
    ");
            return -EFAULT;
        }
        memset(&key_dev->event, 0, sizeof(struct key_event));    
        
        return 0;
    }
    
    // 中断处理程序,多个按键可以根据 irqno 区分  
    irqreturn_t key_irq_handler(int irqno, void *dev_id)  
    {  
        int ret;  
      
        printk("------------%s-------------
    ", __FUNCTION__);  
          
        ret = gpio_get_value(key_info[i].key_gpio);  
        ret ? (key_dev->key.value = 0) : (key_dev->key.value = 1);  
        printk("key = %d status = %d
    ", key_dev->key.name, key_dev->key.value);  
      
        // 返回值一定要是 IRQ_HANDLED  
        return IRQ_HANDLED;  
    }  
      
    static int __init key_drv_init(void)  
    {  
        ... ...  
        key_dev->irqno = IRQ_EINT(2);  
        ret = request_irq(key_dev->irqno, key_irq_handler, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "key_eint", NULL);  
        ... ...  
    }  
      
    static int __exit key_drv_exit(void)  
    {  
        ... ...  
        free_irq(key_dev->irqno,NULL);  
        ... ...  
    } 
     
    
     
    // 应用层
    fd = open("/dev/key0", O_RDWR);

    这种情况下,应用层的 read 会一直不停的读按键值,使用 top 指令查看,发现 cpu 几乎被全部占用。



    2、驱动层实现阻塞

    struct __wait_queue_head {
    spinlock_t lock;
    struct list_head task_list;
    };
    typedef struct __wait_queue_head wait_queue_head_t;

    // 初始化等待队列头
    init_waitqueue_head(wait_queue_head_t *q);


    // 功能:在特定的时候进行休眠
    // 参数1:等待队列头
    // 参数2:条件,条件为假,该代码就会阻塞,如果为真,不做任何事情
    wait_event_interruptible(wait_queue_head_t q,condition);


    // 功能: 资源可达的时候,唤醒
    wake_up_interruptible(wait_queue_head_t *q);

    struct samsung_key{
        int major;
        int irqno;
        struct class *cls;
        struct device *dev;
        struct key_event event;
        unsigned char have_data;
        wait_queue_head_t wq_head;
    };
    struct samsung_key *key_dev;
    
    ssize_t key_drv_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos)
    {
        int ret;
    
        // 判断是否阻塞,条件为假,就会阻塞,让出cpu
        wait_event_interruptible(key_dev->wq_head,key_dev->have_data);
    
        ret = copy_to_user(buf, &key_dev->event, count);
        if(ret > 0)
        {
            printk("copy_to_user error
    ");
            return -EFAULT;
        }
        memset(&key_dev->event, 0, sizeof(struct key_event));
        key_dev->have_data = 0;    
        
        return 0;
    }
    
    // 中断处理程序,多个按键可以根据 irqno 区分  
    irqreturn_t key_irq_handler(int irqno, void *dev_id)  
    {  
        int ret;  
      
        printk("------------%s-------------
    ", __FUNCTION__);  
          
        ret = gpio_get_value(key_info[i].key_gpio);  
        ret ? (key_dev->key.value = 0) : (key_dev->key.value = 1);  
        printk("key = %d status = %d
    ", key_dev->key.name, key_dev->key.value);  
    
        key_dev->have_data = 1;
        // 唤醒
        wake_up_interruptible(&key_dev->wq_head);
      
        // 返回值一定要是 IRQ_HANDLED  
        return IRQ_HANDLED;  
    }  
      
    static int __init key_drv_init(void)  
    {  
        ... ...  
        key_dev->irqno = IRQ_EINT(2);  
        ret = request_irq(key_dev->irqno, key_irq_handler, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "key_eint", NULL); 
        // 初始化等待队列头
        init_waitqueue_head(&key_dev->wq_head);
        ... ...  
    }  
      
    static int __exit key_drv_exit(void)  
    {  
        ... ...  
        free_irq(key_dev->irqno,NULL);  
        ... ...  
    } 
    
    // 应用层
    fd = open("/dev/key0", O_RDWR);

    实现阻塞后,应用层读不到按键值时就会休眠,让出cpu资源



    3、驱动层实现非阻塞

    实现非阻塞很容易,只需在读数据前加判断就行,可以单独实现非阻塞,也可以同时兼容阻塞和非阻塞、

    // 单独实现非阻塞
    ssize_t key_drv_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos)
    {
        int ret;
    
        // 非阻塞,资源不可达的时候,立刻返回,资源可达,直接读写数据
        if( (filp->f_flags & O_NONBLOCK) && !key_dev->have_data)
            return -EAGAIN;
    
        ret = copy_to_user(buf, &key_dev->event, count);
        if(ret > 0)
        {
            printk("copy_to_user error
    ");
            return -EFAULT;
        }
        memset(&key_dev->event, 0, sizeof(struct key_event));
        key_dev->have_data = 0;    
        
        return 0;
    }
    
    // 应用层以非阻塞的方式
    fd = open("/dev/key0", O_RDWR | O_NONBLOCK);

    非阻塞模式下,当应用程序运行后,没有读到按键值,就会收到一个错误(-EAGAIN)返回值。

    // 驱动层兼容阻塞和非阻塞
    ssize_t key_drv_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos)
    {
        int ret;
    
        // 资源不可达的时候,立刻返回,资源可达,直接读写数据
        if( (filp->f_flags & O_NONBLOCK) && !key_dev->have_data)
            return -EAGAIN;
    
        // 判断是否阻塞,条件为假,就会阻塞,让出cpu
        wait_event_interruptible(key_dev->wq_head,key_dev->have_data);
    
        ret = copy_to_user(buf, &key_dev->event, count);
        if(ret > 0)
        {
            printk("copy_to_user error
    ");
            return -EFAULT;
        }
        memset(&key_dev->event, 0, sizeof(struct key_event));
        key_dev->have_data = 0;    
        
        return 0;
    }
  • 相关阅读:
    金融系列10《发卡行脚本》
    金融系列9《发卡行认证》
    金融系列8《应用密文产生》
    ED/EP系列5《消费指令》
    ED/EP系列4《圈存指令》
    ED/EP系列2《文件结构》
    ED/EP系列1《简介》
    社保系列11《ATR》
    社保系列3《文件结构》
    社保系列2《文件系统》
  • 原文地址:https://www.cnblogs.com/lialong1st/p/7756671.html
Copyright © 2020-2023  润新知