• 十、【等待队列】按键中断程序更新


    一、概述

      linux驱动中,阻塞一般就是用等待队列来实现,将进程停止在此处并睡眠下,直到条件满足时,才可通过此处,继续运行。在睡眠等待期间,wake up时,唤起来检查条件,条件满足解除阻塞,不满足继续睡下去。

    上一个按键中断程序中,在读取按键键值时,应用层在read时一直返回键值,并不会阻塞在read,等待有按键按下的时候再返回,在这里我们使用等待队列来进行阻塞。

    二、等待队列相关接口

    1、等待队列的定义

    (1)静态定义

    #define DECLARE_WAIT_QUEUE_HEAD(name) \
    	wait_queue_head_t name = __WAIT_QUEUE_HEAD_INITIALIZER(name)  

    (2)动态定义

    	wait_queue_head_t key_wq;
    	init_waitqueue_head(key_wq);
    	
    	#define init_waitqueue_head(q)				\
    	do {						\
    		static struct lock_class_key __key;	\
    							\
    		__init_waitqueue_head((q), #q, &__key);	\
    	} while (0)
    

    2、等待

    wait_event(wq, condition) -----------------深睡眠,不可被信号打断
    wait_event_interruptible(wq, condition)----浅睡眠,可以被信号打断
    #define wait_event_interruptible(wq, condition)				\
    ({									\
    	int __ret = 0;							\
    	if (!(condition))						\
    		__wait_event_interruptible(wq, condition, __ret);	\
    	__ret;								\
    })
    

     参数:

    • wq:等待队列名字
    • condition:等待的条件,condition假时一直阻塞等待,为真时执行。

    3、唤醒

    wake_up(x)   //和wait_event配套使用
    wake_up_interruptible(x)  //和wait_event_interruptible配套使用 

    三、按键中断中使用等待队列

    key_drv.c

    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/cdev.h>
    #include <linux/fs.h>
    #include <linux/device.h>
    #include <linux/ioport.h>
    #include <linux/io.h>
    #include <linux/uaccess.h>
    #include <linux/types.h>
    #include <linux/gpio.h>
    #include <cfg_type.h>
    #include <linux/interrupt.h>
    #include <linux/miscdevice.h>
    #include <linux/delay.h>
    #include <linux/wait.h>
    #include <linux/sched.h>
    struct key_gpio_t{
    	unsigned int irq;
    	char irqname[20];
    	unsigned char keyvalue;
    };
    static DECLARE_WAIT_QUEUE_HEAD(key_wq) ;   //静态定义等待队列
    static bool flag = false;
    
    static struct key_gpio_t key_gpio[]=
    {
    	{IRQ_GPIO_A_START+28,"KEY2_GPIOA28",2},
    	{IRQ_GPIO_B_START+30,"KEY3_GPIOB30",3},
    	{IRQ_GPIO_B_START+31,"KEY4_GPIOB31",4},
    	{IRQ_GPIO_B_START+9,  "KEY6_GPIOB9",6},
    };
    static char keyvalue = 0;
    ssize_t gec6818_key_read(struct file *filp, char __user * buf, size_t size, loff_t *oft)
    {
    	   
           int ret;
    	  wait_event_interruptible(key_wq, flag);
    	  flag = false;  //唤醒一次队列后要复位flag的值
    	  if(size !=1)
    	  {
    	  	return -EINVAL;
    	  }
    	  ret = copy_to_user(buf, &keyvalue, sizeof(keyvalue));
    	  if(ret != 0)
    	  {
    	  	return (size -ret);
    	  }
    	  keyvalue=0;
    	  return size;
    }
    static irqreturn_t gec6818_key_handler(int irq, void * dev)
    {
       	
    	struct key_gpio_t keytmp=*(struct key_gpio_t *)dev;
    	keyvalue =keytmp.keyvalue;
    	mdelay(400);  //按键防抖
    	flag = true;  //设置flag为true
    	wake_up_interruptible(&key_wq); //按键按下时,唤醒等待队列
    	return IRQ_HANDLED;
    }	
    
    struct file_operations key_misc_fops=
    {
    	.read = gec6818_key_read,
    };
    static struct miscdevice key_misc={
    	.minor = MISC_DYNAMIC_MINOR,
    	.name = "key_misc",
    	.fops =  &key_misc_fops,
    	};
    static int __init  gec6818_key_init(void)
    {
          int ret,i;
           printk(KERN_INFO"gec6818_key_init\n");
          ret = misc_register(&key_misc);
          if(ret < 0)
          {
          		printk(KERN_INFO"key misc register fail.\n");
    		goto misc_register_err;	
          }
    	for(i=0;i<4;i++)
    	{
    		 ret = request_irq(key_gpio[i].irq, gec6818_key_handler,IRQF_TRIGGER_FALLING,key_gpio[i].irqname,(void*)&key_gpio[i]);
    		 if(ret < 0)
    		 {
    		 	printk(KERN_INFO"request_irq fail.\n");
    			goto irq_request_err;
    		 }
    	}
    	return 0;
    
    irq_request_err:
    	while(i--) 
    	{
    		free_irq(key_gpio[i].irq,NULL);
    	}
    misc_register_err:
    		return 0;
    }
    
    static void __exit gec6818_key_exit(void)
    {
    	int i;
        printk(KERN_INFO"gec6818_key_exit\n");
        misc_deregister(&key_misc);	  
          for(i=0;i<4;i++) 
         {
    		free_irq(key_gpio[i].irq,(void *)&key_gpio[i]);
         }
      
    }
    
    module_init(gec6818_key_init);
    module_exit(gec6818_key_exit);
    MODULE_LICENSE("GPL");

     main.c

    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <sys/ioctl.h>
    
    
    int main()
    {
        int fd,ret;
    
        char keyvalue=0;
        fd = open("/dev/key_misc",O_RDWR);
        if(fd<0)
        {
            perror("open key_misc error!");        
        }
        while(1)
        {
            ret=read(fd,&keyvalue,1);
            if(ret !=1)
            {
                perror("read error");
                continue;
            }
    
            printf("keyvalue=key%d\n",keyvalue);
        }
        
         close(fd);
    }
    

      

    相关链接linux中的阻塞机制和等待队列

  • 相关阅读:
    抽象工厂学习笔记
    SQL Transcation的一些总结
    享元模式(Flyweight)
    编程语言简史
    代理模式(Proxy)
    打造属于你的提供者(Provider = Strategy + Factory Method)
    打造属于你的加密Helper类
    单例模式(Singleton)的6种实现
    在C#中应用哈希表(Hashtable)
    InstallShield脚本语言的编写
  • 原文地址:https://www.cnblogs.com/yuanqiangfei/p/15707895.html
Copyright © 2020-2023  润新知