当前位置:首页>Linux>Linux:自定义工作队列的使用

Linux:自定义工作队列的使用

  • 2026-09-12 22:16:13
Linux:自定义工作队列的使用

文章目录

    • 一、工作队列的选择
    • 二、自定义工作队列介绍
      • 2.1、工作队列相关结构体
        • ① work_struct工作项:
        • ② workqueue_struct 工作队列
      • 2.2、工作队列相关接口函数
        • 2.2.1、关键API:创建与添加
          • ① 创建工作队列
          • ② 将工作项添加到队列(源头queue_work_on)
        • 2.2.2、关键API:取消与清理
          • ① 取消已调度的工作
          • ② 强制处理队列中的所有工作
          • ③ 删除工作队列
          • ④ 使用流程总结
    • 三、自定义共享队列案例

    沉淀、分享、成长,让自己和他人都能有所收获!😄

    学习原因


    共享工作队列是全局共享的,任务执行会相互影响,对于有特殊需求的驱动(如需要高优先级、独立调度的任务),需要创建自定义工作队列。

    学习目的

    • 掌握自定义工作队列的创建与使用方法,能为驱动创建独立的工作队列。
    • 理解自定义工作队列的调度机制与优先级设置,避免任务被其他驱动阻塞。
    • 理解自定义工作队列与共享工作队列的区别,能根据场景选择合适的工作队列。

    学习内容

    • 自定义工作队列的创建:create_workqueue()/alloc_workqueue(),设置队列名称、标志、CPU 亲和性。
    • 自定义工作队列的 API:queue_work()、destroy_workqueue()。
    • workqueue_struct结构体解析:内核工作队列的核心结构体,包含工作线程、任务链表等信息。
    • 自定义工作队列的调度:任务会被提交到专属的内核线程执行,不会被其他驱动的任务阻塞。
    • 自定义工作队列的优缺点:优点是独立调度、优先级可控;缺点是会创建额外的内核线程,占用系统资源。

    一、工作队列的选择

    共享队列是操作系统内核统一管理的公共任务池,所有程序都可以使用这个共用的队列来处理任务。而自定义队列则是由操作系统或设备驱动专门创建的独立任务池,用来执行特定的定制化操作。

    简单来说:

    1. 共享队列:系统自带的公共任务通道,大家都可以用
    2. 自定义队列:专门定制的任务通道,为特定功能服务
      两者的主要区别在于:共享队列是系统级通用工具,自定义队列是针对具体需求设计的专用工具。

    二、自定义工作队列介绍

    2.1、工作队列相关结构体

    ① work_struct工作项:

    在 Linux 内核中,结构体 struct work_struct 描述的是要延迟执行的工作项,定义在include/linux/workqueue.h 当中,如下所示:

    struct work_struct{
        atomic_long_t data;
        struct list_head entry;
        work_func_t func;
        #ifdefCONFIG_LOCKDEP
            structlockdep_map lockdep_map;
        #endif
    };

    主要成员:

    • atomic_long_t data:存储工作项数据的字段,支持安全的多线程访问
    • struct list_head entry:工作项在队列中的链表节点,用于将工作项连接到队列中
    • work_func_t func:工作项执行时调用的处理函数

    ② workqueue_struct 工作队列

    这些工作组织成工作队列,内核使用 struct workqueue_struct 结构体描述一个工作队列,定义在 kernel/workqueue.c 当中,如下所示:

    /*
     * The externally visible workqueue.  It relays the issued work items to
     * the appropriate worker_pool through its pool_workqueues.
     */

    struct workqueue_struct{
        struct list_head        pwqs;/* WR: all pwqs of this wq */
        struct list_head        list;/* PR: list of all workqueues */

        struct mutex            mutex;/* protects this wq */
        int                     work_color;/* WQ: current work color */
        int                     flush_color;/* WQ: current flush color */
        atomic_t                nr_pwqs_to_flush;/* flush in progress */
        struct wq_flusher*first_flusher;/* WQ: first flusher */
        struct list_head        flusher_queue;/* WQ: flush waiters */
       struct list_head        flusher_overflow;/* WQ: flush overflow list */

    struct list_head        maydays;/* MD: pwqs requesting rescue */
    struct worker*rescuer;/* I: rescue worker */

    int                     nr_drainers;/* WQ: drain in progress */
    int                     saved_max_active;/* WQ: saved pwq max_active */

    struct workqueue_attrs*unbound_attrs;/* PW: only for unbound wqs */
    struct pool_workqueue*dfl_pwq;/* PW: only for unbound wqs */

    #ifdef CONFIG_SYSFS
    struct wq_device*wq_dev;/* I: for sysfs interface */
    #endif
    #ifdef CONFIG_LOCKDEP
    struct lockdep_map      lockdep_map;
    #endif
    char                    name[WQ_NAME_LEN];/* I: workqueue name */

    /*
             * Destruction of workqueue_struct is sched-RCU protected to allow
             * walking the workqueues list without grabbing wq_pool_mutex.
             * This is used to dump all workqueues from sysrq.
             */

    struct rcu_head         rcu;

    /* hot fields used during command issue, aligned to cacheline */
    unsignedint            flags ____cacheline_aligned;/* WQ: WQ_* flags */
    struct pool_workqueue __percpu *cpu_pwqs;/* I: per-cpu pwqs */
    struct pool_workqueue __rcu *numa_pwq_tbl[];/* PWR: unbound pwqs indexed by node */
    };

    2.2、工作队列相关接口函数

    include/linux/workqueue.h ----- API 声明位置

    2.2.1、关键API:创建与添加

    ① 创建工作队列

    create_workqueue(name)

    • 作用:创建一个工作队列,每个CPU都会对应一个独立的线程。
    • 参数:name 是队列的名字。
    • 返回值:成功返回队列的指针,失败返回NULL。

    create_singlethread_workqueue(name)

    • 作用:创建一个仅单线程的工作队列,不管有多少CPU,只创建一个线程。
    • 参数:name 是队列的名字。
    • 返回值:成功返回队列的指针,失败返回NULL。

    ② 将工作项添加到队列(源头queue_work_on)

    我们经常用的是queue_work。其实底层都是调用queue_work_on

    tatic inline bool queue_work(structworkqueue_struct*wq,
    structwork_struct*work)
    {
        returnqueue_work_on(WORK_CPU_UNBOUND, wq, work);
    }

    queue_work_on(cpu, wq, work)

    • 作用:把一个工作项立即放入指定CPU的队列,等待执行。
    • 参数:
      • cpu:目标CPU编号(如0表示第一个CPU)。
      • wq:目标队列的指针。
      • work:要执行的工作项(需提前初始化)。
    • 返回值:成功返回true,失败返回false。

    2.2.2、关键API:取消与清理

    ① 取消已调度的工作

    cancel_work_sync(work)

    • 作用:取消已放入队列但未执行的工作。
    • 如果工作正在执行:会等待它执行完毕再返回。
    • 参数:work 是要取消的工作项指针。
    • 返回值:成功返回true,失败返回false。

    ② 强制处理队列中的所有工作

    flush_workqueue(wq)

    • 作用:立即执行队列中所有未完成的工作,直到全部处理完毕。
    • 参数:wq 是要处理的队列指针。

    ③ 删除工作队列

    destroy_workqueue(wq)

    • 作用:释放队列资源(如内存和线程)。
    • 前提:必须确保队列中的所有工作已执行完毕(可通过flush_workqueue完成)。
    • 参数:wq 是要删除的队列指针。

    ④ 使用流程总结

    1. 创建队列:用create_workqueue或create_singlethread_workqueue。
    2. 添加工作:用queue_work_on将工作项放入队列。
    3. 取消工作:用cancel_work_sync终止未执行的工作。
    4. 清理队列:用flush_workqueue确保所有工作完成,再用destroy_workqueue删除队列。

    三、自定义共享队列案例

    #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(structwork_struct*work)
    {
        msleep(1000);
        printk("This is test_work\n");
    }

    // 中断处理函数
    irqreturn_ttest_interrupt(int irq,void*args)
    {
        printk("This is test_interrupt\n");
        //schedule_work(&test_workqueue);
        //提交一个任务 给 工作队列  
        //worker ---- 调度者
        queue_work(test_workqueue,&test_workqueue_work);// 提交工作项到工作队列
        returnIRQ_RETVAL(IRQ_HANDLED);
    }

    staticintinterrupt_irq_init(void)
    {
        int ret;
        irq =gpio_to_irq(104);// 将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   ---- workqueue_struct 
        //test_workqueue_work --- work_struct 
        //test_work  --- 函数
        test_workqueue_work=create_workqueue("test_workqueue");// 创建工作队列(比共享工作队列多的一行)
        INIT_WORK(&test_workqueue_work, test_work);// 初始化工作项

        return0;
    }

    staticvoidinterrupt_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");

    最新文章

    随机文章