十、【等待队列】按键中断程序更新
一、概述
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中的阻塞机制和等待队列
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于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用法