等待队列-30
Posted 杨斌并
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了等待队列-30相关的知识,希望对你有一定的参考价值。
等待队列
阻塞与非阻塞的概念
- 阻塞:当前设备如果不可读或不可写时,也就是不能获得资源的时候,那么当前进程程会被挂起。只有当设备满足条件的时候才可以返回。默认情况下,文件都是以这种方式打开。
- 非阻塞:当前设备不可读或不可写时,该函数不会阻塞当前进程,要么放弃,要么不停的查询,或者直到可以操作为止。
读写函数是否阻塞可以通过参数来指定:
fd = open(filepath,O_RDWR);//默认阻塞打开
fd = open(filepath,O_RDWR|O_NONBLOCK); //非阻塞方式打开
等待队列基础知识
当我们进程去访问设备的时候,经常需要等待有特定事件发生以后在继续往下运行,这个时候就需要在驱动里面实现当条件不满足的时候进程休眠,当条件满足的时候在由内核唤醒进程。那么等待队列就实现了在事件上的条件等待。
- 等待队列头
- 等待队列头就是一个等待队列的头部,每个访问设备的进程都是一个队列项,当设备不可用的时候就要将这些进程对应的等待队列项添加到等待队列里面。
- 等待队列头使用结构体wait_queue_head_t来表示,这个结构体定义在文件include/linux/wait里面,结构体内容如下:
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_wq;//定义一个等待队列的头
定义等待队列头以后需要初始化,可以使用init waitqueue_head函数初始化等待队列头,
函数原型如下:
void init_waitqueue_head(wait_queue_head_t *q)
也可以使用宏 DECLARE_WAIT_QUEUE_HEAD来一次性完成等待队列头的定义和初始化。
DECLARE_WAIT_QUEUE_HEAD (wait_queue_head_t*q);
三.等待队列相关函数(#include <linux/wait.h>)
- init_waitqueue_head宏
原型:
void init_waitqueue_head(wait_queue_head_t *q)
作用:
动态初始化等待队列头结构
参数:
- q是wait_queue_head_t 指针
- wait_event宏
原型:
wait_event(wq, condition)
功能:
不可中断的阻塞等待,让调用进程进入不可中断的睡眠状态,在等待队列里面睡眠直到condition变成真,被内核唤醒。
参数:
- wq : wait_queue_head_t类型变量。
- condition为等待的条件,为假时才可以进入休眠
- 注意:调用的时要确认condition值是真还是假,如果调用condition为真,则不会休眠。
- wait_event_interruptible宏
原型:
wait_event_interruptible(wq, condition)
功能:
可中断的阻塞等待,让调用进程进入可中断的睡眠状态,直到condition变成真被内核唤醒或被信号打断唤醒。
参数:
- wq : wait_queue_head_t类型变量。
- condition为等待的条件,为假时才可以进入休眠
- 返回:判断condition是否为真,如果为真则返回,否则检查如果进程是被信号唤醒,会返回-ERESTARTSYS错误码.如果是condition为真,则返回0
- wake_up宏
原型:
wake_up(x)
功能:
唤醒所有休眠进程
参数:
- x:等待队列头结构指针。
- wake_up_interruptible宏
原型:
wake_up_interruptible(x)
功能:
唤醒可中断的休眠进程
参数:
- x:等待队列头结构指针。
代码
- driver.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/interrupt.h>
#include <linux/of_irq.h>
#include <linux/wait.h>
#include <linux/sched.h>
int gpio_num;
int irq = 0;
int value = 0;
int wq_flags = 0;
struct device_node *test_device_node;
struct property *test_node_property;
struct tasklet_struct key_tesk;
DECLARE_WAIT_QUEUE_HEAD (key_wq);
struct of_device_id of_match_table[] = {
{.compatible = "test_keys"},
{}
};
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("hello misc_release bye bye \\n");
return 0;
}
int misc_read(struct file* file, char __user * ubuf, size_t size, loff_t * loff_t){
wait_event_interruptible(key_wq, wq_flags);
printk("hello misc_read \\n");
if(copy_to_user(ubuf, &value, sizeof(value)) != 0 ){
printk("copy_to_user error \\n");
return -1;
}
wq_flags = 0;
return 0;
}
int misc_write(struct file * file, const char __user * ubuf, size_t size, loff_t * loff_t){
printk("hello misc_write \\n");
char kbuf[64] = {0};
if (copy_from_user(kbuf, ubuf, size) != 0)
{
printk("copy_from_to_user error \\n");
return -1;
}
printk("kbuf is %s \\n", kbuf);
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 = "test_wq",
.fops = &misc_fops
};
irq_handler_t test_key(int irq, void *args){
value = !value;
wq_flags = 1;
wake_up(&key_wq);
printk("test_key start \\n");
//tasklet_schedule(&key_tesk);
printk("test_key end \\n");
return IRQ_HANDLED;
}
void test(unsigned long data){
int i = data;
while (i--)
{
printk("task_key long time is %d \\n", i);
}
}
int beep_probe(struct platform_device *pdev){
int ret = 0;
printk("beep_probe 匹配成功了 \\n");
test_device_node = of_find_node_by_path("/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, "touch-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 %d \\n", irq);
ret = request_irq(irq, test_key, IRQF_TRIGGER_RISING, "test_key", NULL);
if (ret < 0)
{
printk("request_irq is error \\n");
return ret;
}
//tasklet_init(&key_tesk, test, 100);
ret = misc_register(&misc_dev);
if (ret <0)
{
printk("misc_registe is error \\n");
}
printk("misc registe is succeed \\n");
return 0;
}
int beep_remove(struct platform_device *pdev){
printk("beep_remove \\n");
return 0;
}
const struct platform_device_id beep_idtable = {
.name = "test_keys"
};
struct platform_driver beep_device =
{
.probe = beep_probe,
.remove = beep_remove,
.driver = {
.name = "123",
.owner = THIS_MODULE,
.of_match_table = of_match_table
},
.id_table = &beep_idtable
};
static int beep_driver_init(void){
printk(KERN_EMERG "hello world enter \\n");
int ret = 0;
ret = platform_driver_register(&beep_device);
if (ret < 0)
{
printk("platform_driver_register 失败\\n");
}
printk("platform_driver_register ok\\n");
return 0;
}
static void beep_driver_exit(void){
printk(KERN_EMERG "hello world exit! \\n");
tasklet_kill(&key_tesk);
free_irq(irq, NULL);
misc_deregister(&misc_dev);
platform_driver_unregister(&beep_device);
}
module_init(beep_driver_init);
module_exit(beep_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("LIYU");
- app.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char * argv[]){
int fd;
int value;
fd = open("/dev/test_wq", O_RDWR);
if (fd <0)
{
perror("open error \\n");
return fd;
}
while (1)
{
read(fd, &value, sizeof(value));
printf("value is %d \\n", value);
}
return 0;
}
设备树的代码
将下面的代码注释
touch-gpio = <&gpio1 20 IRQ_TYPE_EDGE_RISING>;
interrupt-parent = <&gpio1>;
interrupts = <20 IRQ_TYPE_LEVEL_LOW>;
然后在根节点中添加下面的代码
test_key {
compatible = "test_keys";
pinctrl-names = "default";
pinctrl-0 = <&i2c1_xfer>;
reg = <0x38>;
touch-gpio = <&gpio1 20 IRQ_TYPE_EDGE_RISING>;
interrupt-parent = <&gpio1>;
interrupts = <20 IRQ_TYPE_LEVEL_LOW>;
};
以上是关于等待队列-30的主要内容,如果未能解决你的问题,请参考以下文章
JUC并发编程 共享模式之工具 JUC CountdownLatch(倒计时锁) -- CountdownLatch应用(等待多个线程准备完毕( 可以覆盖上次的打印内)等待多个远程调用结束)(代码片段