[Linux内核驱动]工作队列

工作队列

工作队列的使用方法和tasklet类似,但是工作队列的执行上下文是内核线程,可以调度和睡眠。

使用工作队列构造下面数据表,并通过设备读取

   time   delta  inirq    pid   cpu command
     8890    0     0      3813   1   cat
     8890    0     0       108   0   kworker/u14:4
     8890    0     0       108   0   kworker/u14:4
     8890    0     0       108   0   kworker/u14:4
     8890    0     0       108   0   kworker/u14:4

struct work_struct

struct work_struct 是一个用来描述一个可延迟执行的工作的结构体。
使用 INIT_WORK(_work, _func) 宏可以初始化一个 work_struct 实例并与处理函数绑定。

工作队列API

alloc_workqueue

创建一个新的工作队列并返回一个指向新创建的workqueue_struct结构的指针。

struct workqueue_struct *alloc_workqueue(const char *name, int flags, int max_active,...);
  • name:这是一个格式字符串,用于指定工作队列的名称。它可以包含%s、%d等格式化占位符,后跟相应的可变参数(…)。
  • flags:用于配置工作队列的标志位。常见的标志包括:
    • WQ_UNBOUND:工作队列中的工作可以在任何CPU上执行,而不是仅限于创建工作队列的CPU。
    • WQ_MEM_RECLAIM:允许工作队列在内存不足时回收内存。
    • WQ_HIGHPRI:创建一个高优先级的工作队列,其中的工作会比其他工作队列中的工作更频繁地运行。
    • WQ_CPU_INTENSIVE:创建一个CPU密集型的工作队列,该队列中的工作会倾向于在相同的CPU上连续运行。
    • WQ_FREEZABLE:允许工作队列在冻结时暂停执行工作。
  • max_active:工作队列中可以同时运行的最大工作数。如果设置为0,则没有限制。

queue_work

这个函数用于将一个工作(work_struct)添加到工作队列中。当工作队列的调度器线程(或线程池中的某个线程)变得可用时,它会执行这个工作。这个函数接受一个指向workqueue_struct的指针和一个指向work_struct的指针作为参数。

int queue_work(struct workqueue_struct *wq, struct work_struct *work);

destroy_workqueue()

这个函数用于销毁一个工作队列。在销毁之前,它会确保队列中的所有工作都已完成。这个函数接受一个指向workqueue_struct结构的指针作为参数。

void destroy_workqueue(struct workqueue_struct *wq);

代码

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/sched.h>

#include <linux/workqueue.h>

#define WORK_QUEUE_MAJOR 0
#define WORK_QUEUE_MINOR 0
#define WORK_QUEUE_NR_DEVS 1

static int work_queue_major = WORK_QUEUE_MAJOR;
static int work_queue_minor = WORK_QUEUE_MINOR;
static int work_queue_nr_devs = WORK_QUEUE_NR_DEVS;

struct work_queue_dev
{
  struct cdev cdev;
  struct device* cls_dev;
  wait_queue_head_t wq;

  char* buf;
  int loops;
  unsigned long pre_jiffies;
  struct work_struct work;
  struct workqueue_struct* work_queue;
};

static struct class* class = NULL;
static struct work_queue_dev* work_queue_devp;

static int work_queue_open_func(struct inode* inode, struct file* filp)
{
  struct work_queue_dev* dev = container_of(inode->i_cdev, struct work_queue_dev, cdev);
  filp->private_data = dev;
  return 0;
}

static int work_queue_release_func(struct inode* inode, struct file* filp)
{
  return 0;
}

static void work_queue_handeler(struct work_struct* work)
{
  struct work_queue_dev* dev = container_of(work, struct work_queue_dev, work);
  dev->buf += sprintf(dev->buf,
    "%9li  %3li     %i    %6i   %i   %s\n",
    jiffies, jiffies - dev->pre_jiffies,
    in_interrupt() ? 1 : 0, current->pid,
    smp_processor_id(), current->comm);

  dev->loops--;
  if (dev->loops > 0)
  {
    queue_work(dev->work_queue, work);
    dev->pre_jiffies = jiffies;
  }
  else
    wake_up_interruptible(&dev->wq);
}
static ssize_t work_queue_read_func(struct file* filp, char __user* buf, size_t count, loff_t* f_pos)
{
  int ret = 0;
  char* tmp_buf;
  int cnt;
  struct work_queue_dev* dev = filp->private_data;
  if (*f_pos > 0)
    return 0;

  dev->work_queue = alloc_workqueue("work_queue", WQ_UNBOUND, 0);

  tmp_buf = kzalloc(300, GFP_KERNEL);
  if (!tmp_buf)
    return -ENOMEM;
  dev->loops = 4;
  dev->buf = tmp_buf;
  dev->buf += sprintf(dev->buf, "   time   delta  inirq    pid   cpu command\n");
  dev->buf += sprintf(dev->buf,
    "%9li  %3li     %i    %6i   %i   %s\n",
    jiffies, 0L, in_interrupt() ? 1 : 0,
    current->pid, smp_processor_id(), current->comm);
  dev->pre_jiffies = jiffies;

  INIT_WORK(&dev->work, work_queue_handeler);
  queue_work(dev->work_queue, &dev->work);

  wait_event_interruptible(dev->wq, !dev->loops);
  destroy_workqueue(dev->work_queue);

  cnt = dev->buf - tmp_buf;
  if (copy_to_user(buf, tmp_buf, cnt))
  {
    ret = -EFAULT;
    goto out;
  }

  *f_pos += cnt;
  ret = cnt;
out:
  kfree(tmp_buf);
  return ret;
}

struct file_operations work_queue_fops = {
    .owner = THIS_MODULE,
    .read = work_queue_read_func,
    .open = work_queue_open_func,
    .release = work_queue_release_func,
};

static int __init work_queue_init_module(void)
{
  int ret = 0, i;
  dev_t devno = MKDEV(work_queue_major, work_queue_minor);

  if (work_queue_major)
    ret = register_chrdev_region(devno, work_queue_nr_devs, "work_queue");
  else
  {
    ret = alloc_chrdev_region(&devno, work_queue_minor, work_queue_nr_devs, "work_queue");
    work_queue_major = MAJOR(devno);
  }

  if (ret < 0)
  {
    printk(KERN_ALERT "register_chrdev_region failed with %d\n", ret);
    goto out_1;
  }

  work_queue_devp = kzalloc(sizeof(struct work_queue_dev) * work_queue_nr_devs, GFP_KERNEL);
  if (!work_queue_devp)
  {
    ret = -ENOMEM;
    printk(KERN_ALERT "Out of memory\n");
    goto out_2;
  }

  class = class_create(THIS_MODULE, "work_queue_class");
  if (IS_ERR(class))
  {
    ret = PTR_ERR(class);
    printk(KERN_ALERT "class_create failed with %d\n", ret);
    goto out_3;
  }

  for (i = 0; i < work_queue_nr_devs; i++)
  {
    cdev_init(&work_queue_devp[i].cdev, &work_queue_fops);
    work_queue_devp[i].cdev.owner = THIS_MODULE;
    ret = cdev_add(&work_queue_devp[i].cdev, MKDEV(work_queue_major, work_queue_minor + i), 1);
    if (ret < 0)
      printk(KERN_ALERT "cdev_add failed with %d\n", ret);
    else
    {
      work_queue_devp[i].cls_dev = device_create(class, NULL, MKDEV(work_queue_major, work_queue_minor + i), NULL, "work_queue%d", i);
      if (IS_ERR(work_queue_devp[i].cls_dev))
        printk(KERN_ALERT "device_create failed with %d\n", ret);
    }
    init_waitqueue_head(&work_queue_devp[i].wq);
  }
  return 0;
out_3:
  kfree(work_queue_devp);
out_2:
  unregister_chrdev_region(devno, work_queue_nr_devs);
out_1:
  return ret;
}

static void __exit work_queue_exit_module(void)
{
  int i;
  for (i = 0; i < work_queue_nr_devs; i++)
  {
    device_destroy(class, MKDEV(work_queue_major, work_queue_minor + i));
    cdev_del(&work_queue_devp[i].cdev);
  }
  class_destroy(class);
  kfree(work_queue_devp);
  unregister_chrdev_region(MKDEV(work_queue_major, work_queue_minor), work_queue_nr_devs);
}

module_param(work_queue_major, int, S_IRUGO);
module_param(work_queue_minor, int, S_IRUGO);
module_param(work_queue_nr_devs, int, S_IRUGO);

module_init(work_queue_init_module);
module_exit(work_queue_exit_module);

MODULE_AUTHOR("lidonghang-02");
MODULE_LICENSE("GPL");


  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值