- 前言
- 一、等待队列
- 二、相关函数
- 1.wait_queue_head结构体
- 2.init_waitqueue_head
- 3.wait相关的宏
- 三、代码文件
- 1.未加等待队列的c文件
- 2.wait_queue.c文件
- 3.app.c文件
- 4.Makefile文件
- 四、运行结果
- 1.未加等待队列
- 2.加入等待队列
- 总结
本文的主要内容是等待队列的介绍及其相应的案例分析,通过对比未加入等待队列和加入等待队列的输出结果,更好的理解等待队列的作用。
一、等待队列
阻塞:当前设备如果不可读或不可写时,也就是不能获得资源的时候,那么当前进程会被挂起,只有当设备满足条件的时候才可以返回,默认情况下,文件都是以这种方式打开的。
非阻塞:当前设备不可读或不可写时,该函数不会阻塞当前进程,要么放弃,要么不停止的查询,或者直到可以操作为止。
fd = open(filepath,O_RDWR); //默认阻塞打开 fd = open(filepath,O_RDWR|O_NONBLOCK); //非阻塞打开
等待队列:当进程访问设备的时候,经常需要等待有特定事件发生以后再继续往下运行,这个时候就需要在驱动里面实现当条件不满足的时候进行休眠,当条件满足的时候再由内核唤醒进程,那么等待队列就实现了在事件上的条件等待。
等待队列头:等待队列头就是一个等待队列的头部,每个访问设备的进程都是一个队列项,当设备不可用的时候就要将这些进程对应的等待队列项添加到等待队列里面。
二、相关函数 1.wait_queue_head结构体
等待队列头使用结构体wait_queue_head表示,其定义在/linux-4.1.15/include/linux/wait.h文件中。
struct __wait_queue_head {
spinlock_t lock; //自旋锁
struct list_head task_list; //链表头
};
typedef struct __wait_queue_head wait_queue_head_t;
其类型名为wait_queue_head_t。
定义一个等待队列头如下。
wait_queue_head_t test;2.init_waitqueue_head
定义等待队列头以后需要初始化,可以使用init_waitqueue_head函数初始化等待队列头,其定义在
/linux-4.1.15/include/linux/wait.h文件中。
extern void __init_waitqueue_head(wait_queue_head_t *q, const char *name, struct lock_class_key *);
也可以使用宏DECLARE_WAIT_QUEUE_HEAD来一次性完成等待队列头的定义和初始化,语法如下。
DECLARE_WAIT_QUEUE_HEAD(wait_queue_head_t *q);3.wait相关的宏
wait_event宏:
#define __wait_event(wq, condition)
功能:不可中断的阻塞等待,让调用进程进入不可中断的睡眠状态,在等待队列里面睡眠直到condition变为真,才被内核唤醒。
参数介绍:
wq:wait_queue_head_t类型变量。
condition:等待的条件,为假时才可以进入休眠。
注意:调用时若condition为真,则不会休眠。
wait_event_interruptible宏:
#define __wait_event_interruptible(wq, condition)
功能:可中断的阻塞等待,让调用进程进入可中断的睡眠状态,直到condition变成真,才被内核唤醒或者被信号打断唤醒。
wq:wait_queue_head_t类型变量。
condition:等待的条件,为假时才可以进入休眠。
wake_up宏:
wake_up(x)
x:等待队列头结构指针。
功能:唤醒所有休眠进程。
wake_up_interruptible宏:
wake_up_interruptible (x)
x:等待队列头结构指针。
功能:唤醒可中断的休眠进程。
三、代码文件 1.未加等待队列的c文件
#include2.wait_queue.c文件#include #include #include #include #include #include #include #include #include "linux/miscdevice.h" #include "linux/fs.h" #include "linux/uaccess.h" #include "linux/wait.h" #include "linux/sched.h" struct device_node *test_device_node; struct property *test_node_property; int gpio_num; int irq; int value = 0; int misc_open(struct inode *inode, struct file *file) { printk("hello misc_open!n"); return 0; } int misc_release(struct inode *inode, struct file *file) { printk("misc_release bye!n"); return 0; } ssize_t misc_read (struct file *file, char __user *ubuf, size_t size, loff_t *loff_t) { if(copy_to_user(ubuf,&value,sizeof(value))!=0) { printk("copy_to_user error!n"); return -1; } //printk("hello misc_read!n"); return 0; } struct file_operations misc_fops = { //在结构体中引入相关函数 .owner = THIS_MODULE, .open = misc_open, .release = misc_release, .read = misc_read, //.write = misc_write }; struct miscdevice misc_dev={ .minor = MISC_DYNAMIC_MINOR, //次设备号 .name = "misc_device", //设备节点的名字 .fops = &misc_fops }; irq_handler_t test_key(int irq, void *args) { value = ! value; return IRQ_HANDLED; } int dts_probe(struct platform_device *pdev) { int ret = 0; printk("dts_probe matching ok!n"); test_device_node = of_find_node_by_path("/test_key"); //在设备树节点中查找test_key这个节点 if(test_device_node == NULL){ printk("of_find_node_by_path is error!n"); return -1; } gpio_num = of_get_named_gpio(test_device_node,"key-gpio",0); if(gpio_num < 0){ printk("of_get_named_gpio is error!n"); return -1; } gpio_direction_input(gpio_num); //输入 //irq = gpio_to_irq(gpio_num); irq = irq_of_parse_and_map(test_device_node,0); //与上面这句代码的作用相同 printk("irq is %dn", irq); ret = request_irq(irq, test_key, IRQF_TRIGGER_RISING, "test_key", NULL); if(ret < 0){ printk("request_irq is error!n"); return -1; } return 0; } int dts_remove(struct platform_device *pdev) { printk("dts_remove!n"); return 0; } const struct platform_device_id dts_idtable = { .name = "dts_test1" //匹配优先级 第二 }; const struct of_device_id of_match_table_test[] = { {.compatible = "led_keys"}, //匹配优先级 第一 {} }; struct platform_driver dts_device = { .probe = dts_probe, .remove = dts_remove, .driver = { .owner = THIS_MODULE, .name = "dts_test2", //匹配优先级 第三 .of_match_table = of_match_table_test }, .id_table = &dts_idtable }; static int dts_driver_init(void) { int ret = 0; ret = platform_driver_register(&dts_device); if(ret < 0) { printk("platform_driver_register error!n"); return ret; } printk("platform_driver_register ok!n"); ret = misc_register(&misc_dev); if(ret < 0) { printk("miscdevice registered error!n"); return -1; } printk("miscdevice registered successfully!n"); return 0; } static int dts_driver_exit(void) { printk("dts_driver_exit!n"); free_irq(irq,NULL); platform_driver_unregister(&dts_device); misc_deregister(&misc_dev); } module_init(dts_driver_init); module_exit(dts_driver_exit); MODULE_LICENSE("GPL");
#include3.app.c文件#include #include #include #include #include #include #include #include #include "linux/miscdevice.h" #include "linux/fs.h" #include "linux/uaccess.h" #include "linux/wait.h" #include "linux/sched.h" #include #include struct device_node *test_device_node; struct property *test_node_property; DECLARE_WAIT_QUEUE_HEAD(key_wq); // 定义并初始化等待队列头 int gpio_num; int irq; int value = 0; int wq_flags = 0; //标志位 int misc_open(struct inode *inode, struct file *file) { printk("hello misc_open!n"); return 0; } int misc_release(struct inode *inode, struct file *file) { printk("misc_release bye!n"); return 0; } ssize_t misc_read (struct file *file, char __user *ubuf, size_t size, loff_t *loff_t) { wait_event_interruptible(key_wq, wq_flags); if(copy_to_user(ubuf,&value,sizeof(value))!=0) { printk("copy_to_user error!n"); return -1; } wq_flags = 0; return 0; } struct file_operations misc_fops = { //在结构体中引入相关函数 .owner = THIS_MODULE, .open = misc_open, .release = misc_release, .read = misc_read, //.write = misc_write }; struct miscdevice misc_dev={ .minor = MISC_DYNAMIC_MINOR, //次设备号 .name = "misc_device", //设备节点的名字 .fops = &misc_fops }; irq_handler_t test_key(int irq, void *args) { value = ! value; wq_flags = 1; //这里只有先置1,下面的函数才能唤醒 wake_up(&key_wq); //唤醒 return IRQ_HANDLED; } int dts_probe(struct platform_device *pdev) { int ret = 0; printk("dts_probe matching ok!n"); test_device_node = of_find_node_by_path("/test_key"); //在设备树节点中查找test_key这个节点 if(test_device_node == NULL){ printk("of_find_node_by_path is error!n"); return -1; } gpio_num = of_get_named_gpio(test_device_node,"key-gpio",0); if(gpio_num < 0){ printk("of_get_named_gpio is error!n"); return -1; } gpio_direction_input(gpio_num); //输入 //irq = gpio_to_irq(gpio_num); irq = irq_of_parse_and_map(test_device_node,0); //与上面这句代码的作用相同 printk("irq is %dn", irq); ret = request_irq(irq, test_key, IRQF_TRIGGER_RISING, "test_key", NULL); if(ret < 0){ printk("request_irq is error!n"); return -1; } return 0; } int dts_remove(struct platform_device *pdev) { printk("dts_remove!n"); return 0; } const struct platform_device_id dts_idtable = { .name = "dts_test1" //匹配优先级 第二 }; const struct of_device_id of_match_table_test[] = { {.compatible = "led_keys"}, //匹配优先级 第一 {} }; struct platform_driver dts_device = { .probe = dts_probe, .remove = dts_remove, .driver = { .owner = THIS_MODULE, .name = "dts_test2", //匹配优先级 第三 .of_match_table = of_match_table_test }, .id_table = &dts_idtable }; static int dts_driver_init(void) { int ret = 0; ret = platform_driver_register(&dts_device); if(ret < 0) { printk("platform_driver_register error!n"); return ret; } printk("platform_driver_register ok!n"); ret = misc_register(&misc_dev); if(ret < 0) { printk("miscdevice registered error!n"); return -1; } printk("miscdevice registered successfully!n"); return 0; } static int dts_driver_exit(void) { printk("dts_driver_exit!n"); free_irq(irq,NULL); platform_driver_unregister(&dts_device); misc_deregister(&misc_dev); } module_init(dts_driver_init); module_exit(dts_driver_exit); MODULE_LICENSE("GPL");
#include4.Makefile文件#include #include #include #include int main(int argc, char *argv[]) { int fd; int value; fd = open("/dev/misc_device", O_RDWR); if(fd < 0){ perror("open errorn"); return fd; } while(1){ read(fd, &value, sizeof(value)); //printf("value is %dn", value); //在后台运行时要注释掉本行代码 } return 0; }
obj-m += wait_queue.o KDIR:=/linux/linux-4.1.15 PWD?=$(shell pwd) all: make -C $(KDIR) M=$(PWD) modules clean: make -C $(KDIR) M=$(PWD) clean
四、运行结果 1.未加等待队列
先加载驱动如下。
然后运行app,结果如下图。
在app运行时,按下按键,数值翻转一次,如下图。(打印速率真的很快,滚轮滚了好久才找到按键按下的地方)
在原有的app程序中注释掉打印的代码,重新编译,发送到开发板。
使用如下命令让app程序在后台运行。
./app &
按下回车键,然后使用top命令打开资源管理器查看CPU的占用情况。
可以看到,app程序的CPU占用率居然如此高。
CTRL+C退出,然后使用如下命令杀死后台正在运行的app程序。
killall app
再使用top命令打开资源管理器查看CPU的占用情况,发现app程序已经被杀死了。
根据以上的打印结果可知,没有按键按下时,程序也一直在读取数值,这样就使得CPU的占用率很高,因此,在程序中加入等待队列是必要的。
如下图,加入等待队列之后再运行app,程序会阻塞,直到按键按下,程序才会响应进而执行打印操作。
按照代码,每按一次按键,value的值就翻转一次。
现在再次注释掉app中的打印代码,然后后台运行app程序,查看CPU的资源占用情况。
不知道是啥情况,我打开资源管理器找不到后台运行的app,不过没关系,可以肯定的是,加入等待队列之后app程序的CPU占用率一定不高。
总结
以上就是Linux下的等待队列及其案例的所有内容了,通过实验案例我们知道,使用等待队列可以大大的降低CPU的占用率,这对CPU同时处理其他程序相当有益。
本文参考视频:https://www.bilibili.com/video/BV1Vy4y1B7ta?p=38。



