驱动-并发管理工作队列

提示:中断终篇 中断队列中的 并发管理工作队列


前言

假设已经了解了共享工作队列,自定义工作队列,突然有个知识点 并发管理工作队列。 从字眼上看就是并发处理相关的知识点吧。

个人理解:当我们熟悉这个知识点后,在实际应用上面和自定义工作队列就是api 上面创建不一样而已。 核心还是在于这样创建 并发管理工作队列 的意义、内核处理方式需要熟悉。

并发管理工作队列,字面上讲 其实是一种队列的类型,和自定义工作队列一样定义,创建方式不一样而已。

一、 参考资料

Linux kernel workqueue机制分析
并发管理的工作队列 (cmwq)
Linux 内核中的工作队列:alloc_ordered_workqueue

讲解并发管理工作队列概念性内容还是需要自己去理解,参考资料可以多看看,自己理解下。 实际上 如前言所讲:和自定义队列就是创建时候api 方法不一样而已,但是对应的内核机制完全不一样。

二、源码驱动程序

源码驱动程序如下,测试验证:

#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/workqueue.h>

int irq;
struct workqueue_struct *test_workqueue;
struct work_struct test_workqueue_work;

// 工作项处理函数
void test_work(struct work_struct *work)
{
  msleep(1000);
  printk("This is test_work\n");
}

// 中断处理函数
irqreturn_t test_interrupt(int irq, void *args)
{
  printk("This is test_interrupt\n");
  queue_work(test_workqueue, &test_workqueue_work); // 提交工作项到工作队列
  return IRQ_RETVAL(IRQ_HANDLED);
}

static int interrupt_irq_init(void)
{
  int ret;
  irq = gpio_to_irq(101); // 将GPIO映射为中断号
  printk("irq is %d\n", irq);

  // 请求中断
  ret = request_irq(irq, test_interrupt, IRQF_TRIGGER_RISING, "test", NULL);
  if (ret < 0)
  {
    printk("request_irq is error\n");
    return -1;
  }
  // 用于创建和分配一个工作队列
  test_workqueue = alloc_workqueue("test_workqueue", WQ_UNBOUND, 0);
  INIT_WORK(&test_workqueue_work, test_work); // 初始化工作项

  return 0;
}

static void interrupt_irq_exit(void)
{
  free_irq(irq, NULL);                    // 释放中断
  cancel_work_sync(&test_workqueue_work); // 取消工作项
  flush_workqueue(test_workqueue);        // 刷新工作队列
  destroy_workqueue(test_workqueue);      // 销毁工作队列
  printk("bye bye\n");
}

module_init(interrupt_irq_init);
module_exit(interrupt_irq_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("fangchen");

实验结果如下:

在这里插入图片描述

三、alloc_workqueue与create_workqueue的比较与使用

在Linux内核驱动开发中,alloc_workqueue()和create_workqueue()都是用于创建工作队列的函数,但它们有一些重要区别。

函数对比

特性alloc_workqueue()create_workqueue()
引入版本2.6.36+2.6.12+
功能更灵活,支持多种标志较简单
并发控制支持精细控制固定并发
推荐使用新代码推荐旧代码兼容
内存回收支持明确通过WQ_MEM_RECLAIM标志自动支持
CPU绑定可通过WQ_UNBOUND控制默认绑定

alloc_workqueue() 详解

函数原型

struct workqueue_struct *alloc_workqueue(const char *fmt, unsigned int flags, int max_active);

常用标志组合

// 标准工作队列(可睡眠,内存可回收)
alloc_workqueue("name", WQ_MEM_RECLAIM, max_active);

// 高优先级工作队列
alloc_workqueue("name", WQ_MEM_RECLAIM | WQ_HIGHPRI, max_active);

// 非绑定CPU的工作队列(适合CPU密集型任务)
alloc_workqueue("name", WQ_UNBOUND | WQ_MEM_RECLAIM, max_active);

标志说明

  • WQ_MEM_RECLAIM: 内存紧张时可回收
  • WQ_HIGHPRI: 高优先级工作队列
  • WQ_UNBOUND: 不绑定到特定CPU
  • WQ_FREEZABLE: 可被系统冻结
  • WQ_CPU_INTENSIVE: CPU密集型任务

create_workqueue() 详解

函数原型

struct workqueue_struct *create_workqueue(const char *name);

等效的alloc_workqueue表示

// create_workqueue("name") 等效于:
alloc_workqueue("name", WQ_MEM_RECLAIM, 1);

使用示例- alloc_workqueue - create_workqueue

#include <linux/workqueue.h>

struct workqueue_struct *wq;
struct work_struct work;

void work_handler(struct work_struct *work) {
    printk(KERN_INFO "Work handler executing\n");
}

static int __init my_init(void) {
    // 创建最大并发数为4的工作队列
    wq = alloc_workqueue("my_wq", WQ_MEM_RECLAIM | WQ_UNBOUND, 4);
    if (!wq)
        return -ENOMEM;
    
    INIT_WORK(&work, work_handler);
    queue_work(wq, &work);
    
    return 0;
}

static void __exit my_exit(void) {
    flush_workqueue(wq);    // 等待所有工作完成
    destroy_workqueue(wq);  // 销毁工作队列
}








#include <linux/workqueue.h>

struct workqueue_struct *wq;
struct work_struct work;

void work_handler(struct work_struct *work) {
    printk(KERN_INFO "Work handler executing\n");
}

static int __init my_init(void) {
    wq = create_workqueue("my_old_wq");
    if (!wq)
        return -ENOMEM;
    
    INIT_WORK(&work, work_handler);
    queue_work(wq, &work);
    
    return 0;
}

static void __exit my_exit(void) {
    flush_workqueue(wq);
    destroy_workqueue(wq);
}

如何选择-创建中断队列

  • 新代码:总是使用alloc_workqueue(),它提供更多控制和灵活性

  • 维护旧代码:如果已有代码使用create_workqueue(),可以保持不动

  • 特殊需求:需要CPU绑定:使用alloc_workqueue()不带WQ_UNBOUND;需要高优先级:使用WQ_HIGHPRI标志;需要精细并发控制:使用alloc_workqueue()的max_active参数

性能考虑

并发数选择:

  • I/O密集型:可以设置较高的max_active
  • CPU密集型:设置较低的max_active

CPU绑定:

  • 绑定CPU(非WQ_UNBOUND)可以减少缓存失效
  • 非绑定CPU(WQ_UNBOUND)可以更好利用多核

内存压力:

  • 总是设置WQ_MEM_RECLAIM除非有特殊原因

常见问题

Q: 什么时候需要使用flush_workqueue()?

A: 在销毁工作队列前或需要确保所有工作完成时使用。注意:不能在原子上下文中调用。

Q: max_active设置为0是什么意思?

A: 表示使用默认值,通常是每CPU256个worker线程。

四、最佳实践

适合自定义工作队列的场景

  • 需要严格隔离的实时驱动
  • 必须保证特定优先级的任务
  • 运行在较旧内核版本(2.6.36之前)
  • 有特殊调度需求的专用硬件

适合CMWQ的场景

  • 通用设备驱动
  • 新内核版本(2.6.36+)开发
  • 需要良好扩展性的高负载场景
  • 希望减少系统线程总数的设计

性能对比

特性自定义工作队列CMWQ
线程管理静态动态
系统开销较高较低
延迟确定性较好一般
多核扩展性优秀
内存回收支持需手动实现内置支持

总结

这里进一步了解了终端中自定义队列的并发管理工作队列,后面开发中会大量用到的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

野火少年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值