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

一、概述

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

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

二、等待队列相关接口

1、等待队列的定义

(1)静态定义

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

(2)动态定义

1
2
3
4
5
6
7
8
9
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、等待

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

 参数:

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

3、唤醒

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

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

key_drv.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#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中的阻塞机制和等待队列

posted @   轻轻的吻  阅读(94)  评论(0编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
历史上的今天:
2017-12-19 crontab定时作业
2017-12-19 linux常用命令
2017-12-19 genstr.py
2017-12-19 vim用法
2017-12-19 Linux 查看CPU信息,机器型号,内存等信息
2017-12-19 grep基础用法
2017-12-19 awk用法
点击右上角即可分享
微信分享提示