[Linux内核驱动]等待队列

等待队列

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_ */
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值