等待队列
Linux内核的等待队列(Wait Queue)是重要的数据结构,与进程调度机制紧密相关联,可以用来同步对系统资源的访问、异步事件通知、跨进程通信等。
在Linux中,等待队列以循环链表为基础结构,包括两种数据结构:等待队列头(wait queue head)和等待队列元素(wait queue),整个等待队列由等待队列头进行管理。
使用
#include <linux/wait.h>
// 定义等待队列头
wait_queue_head_t wq;
// 初始化
init_waitqueue_head(&wq);
/*上面两步可以直接使用下面宏完成*/
DECLARE_WAIT_QUEUE_HEAD(wq);
// 定义等待队列元素
DECLARE_WAITQUEUE(name, tsk);
// 一般将当前进程定义成名为wait的等待队列元素
DECLARE_WAITQUEUE(wait, current);
// 添加/移除等待队列
add_wait_queue(&wq, &wait);
remove_wait_queue(&wq, &wait);
// 等待事件
wait_event(queue, condition);
wait_event_interruptible(queue, condition);
wait_evect_timeout(queue, condition, timeout)
wait_event_interruptible_timeout(queue, condition, timeout);
// queue 是等待队列头,condition 是等待条件,timeout 是超时时间
// _interruptible可以被信号打断,_timeout意味阻塞等待的超时时间
// 唤醒队列
// wq为上面定义的等待队列头
wake_up(&wq);
wake_up_interruptible(&wq);
代码
wait_queue.c
/*
* @Date: 2024-05-08 13:55:54
* @author: lidonghang-02 2426971102@qq.com
* @LastEditTime: 2024-05-19 20:07:16
*/
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/sched.h>
#include <linux/mutex.h>
#include <linux/device.h>
#include <linux/wait.h>
#include "wait_queue.h"
#define WAIT_QUEUE_MAJOR 0
#define WAIT_QUEUE_MINOR 0
#define WAIT_QUEUE_NR_DEVS 1
static int wq_major = WAIT_QUEUE_MAJOR;
static int wq_minor = WAIT_QUEUE_MINOR;
static int wq_nr_devs = WAIT_QUEUE_NR_DEVS;
static struct class *wq_class;
struct wait_queue_dev
{
struct cdev cdev;
char *s;
int len;
struct device *class_dev;
struct mutex mutex;
};
static struct wait_queue_dev *wq_devp;
DECLARE_WAIT_QUEUE_HEAD(wq);
static int wq_open_func(struct inode *inode, struct file *filp)
{
struct wait_queue_dev *dev;
dev = container_of(inode->i_cdev, struct wait_queue_dev, cdev);
filp->private_data = dev;
printk(KERN_INFO "wait_queue_open\n");
return 0;
}
static ssize_t wq_read_func(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
int ret = 0;
struct wait_queue_dev *dev = filp->private_data;
wait_event_interruptible(wq, dev->s != NULL);
if (*f_pos >= dev->len)
{
printk(KERN_INFO "wait_queue_read: empty\n");
goto out_err_1;
}
if (count > dev->len - *f_pos)
count = dev->len - *f_pos;
if (copy_to_user(buf, dev->s, count))
{
ret = -EFAULT;
goto out_err_1;
}
*f_pos += count;
printk(KERN_INFO "wait_queue_read\n");
return count;
out_err_1:
return ret;
}
static ssize_t wq_write_func(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
int ret = 0;
struct wait_queue_dev *dev = filp->private_data;
if (mutex_lock_interruptible(&dev->mutex))
{
ret = -ERESTARTSYS;
goto out_err_1;
}
kfree(dev->s);
dev->s = NULL;
dev->s = kzalloc(count, GFP_KERNEL);
if (dev->s == NULL)
{
ret = -ENOMEM;
goto out_err_2;
}
dev->len = count;
if (copy_from_user(dev->s, buf, count))
{
ret = -EFAULT;
goto out_err_3;
}
mutex_unlock(&dev->mutex);
wake_up_interruptible(&wq);
printk(KERN_INFO "wait_queue_write\n");
return count;
out_err_3:
kfree(dev->s);
dev->s = NULL;
out_err_2:
mutex_unlock(&dev->mutex);
out_err_1:
return ret;
}
static long wq_ioctl_func(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret = 0;
struct wait_queue_dev *dev = filp->private_data;
// 检查幻数(返回值POSIX标准规定,也用-EINVAL)
if (_IOC_TYPE(cmd) != IOCTL_CHR_MAGIC)
return -ENOTTY;
// 检查命令编号
if (_IOC_NR(cmd) > WQ_MAXNR)
return -ENOTTY;
// 检查命令方向,并验证用户空间指针的访问权限。
if (_IOC_DIR(cmd) & _IOC_READ)
ret = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));
else if (_IOC_DIR(cmd) & _IOC_WRITE)
ret = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));
if (ret)
return -EFAULT;
switch (cmd)
{
case WQ_CLEAN:
kfree(dev->s);
dev->s = NULL;
dev->len = 0;
break;
default:
return -ENOTTY;
}
return ret;
}
static int wq_release_func(struct inode *inode, struct file *filp)
{
printk(KERN_INFO "wait_queue_release\n");
return 0;
}
struct file_operations wq_fops =
{
.owner = THIS_MODULE,
.open = wq_open_func,
.read = wq_read_func,
.write = wq_write_func,
.release = wq_release_func,
.unlocked_ioctl = wq_ioctl_func,
};
static int __init wq_init_module(void)
{
int ret = 0, i;
dev_t devno = MKDEV(wq_major, wq_minor);
if (wq_major)
ret = register_chrdev_region(devno, wq_nr_devs, "wait_queue");
else
{
ret = alloc_chrdev_region(&devno, wq_minor, wq_nr_devs, "wait_queue");
wq_major = MAJOR(devno);
}
if (ret < 0)
{
ret = -ENODEV;
goto out_err_1;
}
wq_devp = kzalloc(sizeof(struct wait_queue_dev) * wq_nr_devs, GFP_KERNEL);
if (wq_devp == NULL)
{
ret = -ENOMEM;
goto out_err_2;
}
wq_class = class_create(THIS_MODULE, "wait_queue");
if (IS_ERR(wq_class))
{
printk(KERN_WARNING "fail create class");
goto out_err_3;
}
for (i = 0; i < wq_nr_devs; i++)
{
wq_devp[i].s = NULL;
wq_devp[i].len = 0;
cdev_init(&wq_devp[i].cdev, &wq_fops);
wq_devp[i].cdev.owner = THIS_MODULE;
ret = cdev_add(&wq_devp[i].cdev, MKDEV(wq_major, wq_minor + i), 1);
if (ret < 0)
printk(KERN_WARNING "fail add hc_dev%d", i);
else
{
wq_devp[i].class_dev = device_create(wq_class, NULL, MKDEV(wq_major, wq_minor + i), NULL, "wait_queue%d", i);
if (IS_ERR(wq_devp[i].class_dev))
{
printk(KERN_WARNING "fail create device for wait_queue%d", i);
cdev_del(&wq_devp[i].cdev);
}
}
mutex_init(&wq_devp[i].mutex);
}
printk(KERN_INFO "wait_queue init success\n");
return 0;
out_err_3:
kfree(wq_devp);
out_err_2:
unregister_chrdev_region(devno, wq_nr_devs);
out_err_1:
return ret;
}
static void __exit wq_exit_module(void)
{
int i;
for (i = 0; i < wq_nr_devs; i++)
{
device_destroy(wq_class, MKDEV(wq_major, wq_minor + i));
cdev_del(&wq_devp[i].cdev);
}
class_destroy(wq_class);
kfree(wq_devp);
unregister_chrdev_region(MKDEV(wq_major, wq_minor), wq_nr_devs);
printk(KERN_INFO "wait_queue exit\n");
}
module_param(wq_major, int, S_IRUGO);
module_param(wq_minor, int, S_IRUGO);
module_param(wq_nr_devs, int, S_IRUGO);
module_init(wq_init_module);
module_exit(wq_exit_module);
MODULE_AUTHOR("lidonghang-02");
MODULE_LICENSE("GPL");
wait_queue.h
#ifndef _WQ_CHR_H_
#define _WQ_CHR_H_
#define IOCTL_CHR_MAGIC 'c'
#define WQ_CLEAN _IO(IOCTL_CHR_MAGIC, 0)
#define WQ_MAXNR 0
#endif /* _WQ_CHR_H_ */