当前位置: 首页 > article >正文

linux 基础知识点之工作队列workqueue

        多年前就了解了workqueue着玩意,但理解上就并不是很很深刻,今天重新梳理一下,本文重点的是哪个些现成的demo代码,都是可以直接拿来用的,这就是写这文章的目的和作用,就是为了备份后续工作用到的时候,可以快速Ctrl+c and Ctrl + v,接下来复制些前辈写好的理解概念,再结合一下个所整理得demo。

一、workqueue工作队列简介

        工作队列是除软中断和tasklet以外最常用的一种下半部机制,其基本原理是:把work(需要推迟执行的函数)交由一个内核线程来执行,工作队列总是在进程上下文执行。                               工作队列 ( workqueue )是将操作(或回调)延期异步执行的一种机制。工作队列可以把工作推后,交由一个内核线程去执行,并且工作队列是执行在线程上下文中,因此工作执行过程中可以被重新调度、抢占、睡眠。

​ 工作队列的优点:

    因工作队列在进程下文中执行,因此工作队列允许重新调度和睡眠,是异步执行的进程上下文。
    解决了如果软中断和tasklet执行时间过长会导致系统实时性下降等问题。

(1-1)work_struct工作

在 Linux 内核中,work_struct 是工作队列(workqueue)机制的核心数据结构,用于表示一个工作项。工作队列允许内核将任务推迟到其他线程中执行,这在处理中断或其他需要快速响应的场景中非常有用

struct my_work {
    struct work_struct work;
    int data;
};

定义一个包含 work_struct 的结构体,用于存储工作项的数据

linux内核中使用work_struct结构体来表示一个工作,如下定义(/inlcude/linux/workqueue.h):

struct work_struct {
	atomic_long_t data;
	struct list_head entry;
	work_func_t func;
#ifdef CONFIG_LOCKDEP
	struct lockdep_map lockdep_map;
#endif
};

(1-2)workqueue工作队列

把工作(包括该工作任务的执行回调函数)添加到一个队列,称为workqueue,即工作队列,然后通过worker-pool中的内核工作线程(worker)去执行这个回调函数。工作队列使用workqueue_struct结构体来表示,定义如下(/kernel/workqueue.c):

    创建工作队列:

    my_workqueue = create_workqueue("my_workqueue");

    使用 create_workqueue 创建一个工作队列,名称为 "my_workqueue"


(1-3)worker工作者线程

​ linux 内核使用工作者线程(worker thread)来处理工作队列中的各个工作,linux 内核使用
worker 结构体表示工作者线程,worker 结构体定义如下(/kernel/workqueue_internal.h)
  
二、workqueue工作队列的使用

​ 每一个worker工作线程中都有一个工作队列,工作线程处理自己工作队列中的所有工作。

在实际开发中,推荐使用默认的workqueue·工作队列,而不是新创建workqueue。使用方法如下:

​ 直接定义一个work_struct结构体变量,然后使用INIT_WORK宏来完成初始化工作,INIT_WORK定义如下:

#define INIT_WORK(_work, _func)

_work表示要初始化的工作,_func是工作对应的处理函数。

也可以使用 DECLARE_WORK 宏一次性完成工作的创建和初始化,宏定义如下:

#define DECLARE_WORK(n, f)

​ n 表示定义的工作(work_struct),f 表示工作对应的处理函数。和 tasklet 一样,工作也是需要调度才能运行的,工作的调度函数为schedule_work(),函数原型如下所示:

bool schedule_work(struct work_struct *work) //其实你就理解为工作队列启动回调的函数

使用cancel_work_sync()取消一个工作,函数原型如下所示:

bool cancel_work_sync(struct work_struct *work)

当然也可以自己创建一个workqueue,特别是网络子系统、块设备子系统情况下等。具体步骤如下:

    使用alloc_workqueue()创建新的workqueue。
    使用INIT_WORK()宏声明一个work和该work的回调函数。
    使用queue_work()在新的workqueue上调度一个work //其实就理解为工作队列启动回调的函数
    使用flush_workqueue()去flush 工作队列上的所有work。

来个demo:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/time.h>

#include <linux/jiffies.h>
#include <linux/workqueue.h>
#include <linux/slab.h> //kmalloc kfree

#include <linux/sched.h>
#include <linux/delay.h>

#define WAIT_TIMES 5000

static char data[] = "[Tab] test for demo work";

struct work_ctx{
	struct work_struct real_work;
	char *str;
	int arg;
}work_ctx;

struct work_ctx *demo_work;

// 定义工作队列
static struct workqueue_struct *my_workqueue;

static void demo_work_func(struct work_struct *work){

	struct work_ctx *temp_work = container_of(work,struct work_ctx,real_work);
	printk(KERN_INFO "[work]=> PID: %d; NAME: %s\n", current->pid, current->comm);
	printk(KERN_INFO "[work]=> sleep 1 seconds\n");
	set_current_state(TASK_INTERRUPTIBLE);
	schedule_timeout(1 * HZ); //Wait 1 seconds
	printk(KERN_INFO "[work]=> data is: %d  %s\n", temp_work->arg,temp_work->str);
}

static int __init demo_thread_init(void){
	int count = 10;
    // 创建工作队列
    my_workqueue = create_workqueue("my_workqueue");
    if (!my_workqueue) {
        return -ENOMEM;
    }    
	demo_work = kmalloc(sizeof(*demo_work),GFP_KERNEL);	
	INIT_WORK(&demo_work->real_work, demo_work_func);
	demo_work->str = data;
	while(count--){
		//msleep(5000);
        msleep(WAIT_TIMES);
        //mdelay(WAIT_TIMES);
		demo_work->arg = count;
		//schedule_work(&demo_work->real_work);
        // 提交工作项到工作队列
        //queue_work(my_workqueue, &my_work->work);        
        queue_work(my_workqueue, &demo_work->real_work);

	}
	return 0;
}

module_init(demo_thread_init);

static void __exit demo_thread_exit(void){
	flush_work(&demo_work->real_work);
	kfree(demo_work);
}
module_exit(demo_thread_exit);

MODULE_LICENSE("GPL");	

除此之外,linux内核还提供了一个workqueue机制与timer机制相结合的延时机制—delayed_work。
Talk is cheap,show me the code

结合hrtimer_demo升级了一下:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/hrtimer.h>
#include <linux/ktime.h>

#include <linux/time.h>

#include <linux/jiffies.h>
#include <linux/workqueue.h>
#include <linux/slab.h> //kmalloc kfree

#include <linux/sched.h>
#include <linux/delay.h>

static struct hrtimer my_timer;
static int count = 0;

#define WAIT_TIMES 5000

static char data[] = "[Tab] test for demo work";

struct work_ctx{
	struct work_struct real_work;
	char *str;
	int arg;
}work_ctx;

struct work_ctx *demo_work;

// 定义工作队列
static struct workqueue_struct *my_workqueue;

static void demo_work_func(struct work_struct *work){

	struct work_ctx *temp_work = container_of(work,struct work_ctx,real_work);
	printk(KERN_INFO "[work]=> PID: %d; NAME: %s\n", current->pid, current->comm);
	printk(KERN_INFO "[work]=> sleep 1 seconds\n");
	set_current_state(TASK_INTERRUPTIBLE);
	schedule_timeout(1 * HZ); //Wait 1 seconds
	printk(KERN_INFO "[work]=> data is: %d  %s\n", temp_work->arg,temp_work->str);
}


#if 1
enum hrtimer_restart my_timer_callback(struct hrtimer *timer)
{
    count++;
    //pr_info("Timer expired! Count: %d\n", count);
    printk("enter [%s] line = %d, count = %d. \n", __func__, __LINE__, count);

    demo_work->arg = count;
    schedule_work(&demo_work->real_work);
    // 提交工作项到工作队列   
    //queue_work(my_workqueue, &demo_work->real_work); 
       
    // 重新启动定时器,设置下次到期时间为 1 秒
    hrtimer_forward_now(timer, ms_to_ktime(2000));
    //调整高分辨率定时器(high-resolution timer,hrtimer)到期时间的一个函数
    return HRTIMER_RESTART; // 代表重复定时器 //重启方式
}
#else

enum hrtimer_restart my_timer_callback(struct hrtimer *timer)
{
    count++;
    //pr_info("Timer expired! Count: %d\n", count);
    printk("enter [%s] line = %d, count = %d. \n", __func__, __LINE__, count);

    // 重新启动定时器,设置下次到期时间为 1 秒
    hrtimer_forward_now(timer, ms_to_ktime(2000));
    //调整高分辨率定时器(high-resolution timer,hrtimer)到期时间的一个函数
    return HRTIMER_RESTART; // 代表重复定时器 //重启方式
}

enum hrtimer_restart my_timer_callback(struct hrtimer *timer)
{
    pr_info(KERN_INFO "High-resolution timer expired after 1 second\n");
    printk("enter [%s] line = %d, count = %d. \n", __func__, __LINE__, count);

    return HRTIMER_NORESTART; // 代表不重复定时器
}
#endif

static int __init my_module_init(void)
{
    ktime_t ktime;
    printk("enter [%s] line = %d \n", __func__, __LINE__);
    pr_info("High-Resolution Timer Example Module Loaded\n");

    // 创建工作队列
    my_workqueue = create_workqueue("my_workqueue");
    if (!my_workqueue) {
        return -ENOMEM;
    }    
	demo_work = kmalloc(sizeof(*demo_work),GFP_KERNEL);	
	INIT_WORK(&demo_work->real_work, demo_work_func);
	demo_work->str = data;

    // 设置定时器初始时间为 1 秒
    ktime = ktime_set(10, 0); // 1 秒, 0 纳秒
    hrtimer_init(&my_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);//初始化高分辨率定时器的函数
    my_timer.function = my_timer_callback;// 定时到期时的回调函数
    hrtimer_start(&my_timer, ktime, HRTIMER_MODE_REL);//启动高分辨率定时器的函数

    return 0;
}

static void __exit my_module_exit(void)
{
    // 停止定时器
    hrtimer_cancel(&my_timer);
    pr_info("High-Resolution Timer Example Module Unloaded\n");
}

module_init(my_module_init);
module_exit(my_module_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("High-Resolution Timer Example");
MODULE_AUTHOR("Pan Shuai");


                        原文链接:https://blog.csdn.net/iriczhao/article/details/122870227

 

Linux内核中的工作队列包括:共享工作队列和自定义工作队列。区别如下:

    1)共享工作队列:将新创建的工作任务添加到Linux内核创建的全局工作队列system_wq中,无需自己创建工作队列;

    2)自定义工作队列:将新创建的工作任务添加到自己创建的工作队列中;

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/kobject.h>
#include <linux/list.h>
#include <linux/kthread.h>
#include <asm/ptrace.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/interrupt.h>

int data  = 10;
 
static struct work_struct       work1;
static struct work_struct       work2;
 
 
 
static void do_work1(struct work_struct *arg)
{
    printk(KERN_INFO "do_work1 .....");
}
 
static void do_work2(struct work_struct *arg)
{
     printk(KERN_INFO "do_work2 .....");
}
 
int threadfn(void *data)
{
     static int count =  0 ;
     int args = *(int *)data;
     printk(KERN_INFO "enter thead_fn");
     while(1)
     {
         msleep(2*1000);
         printk(KERN_INFO "threadfn data: %d, count: %d",args , ++count);
         schedule_work(&work1);
         schedule_work(&work2);
         
     }
} 
 
 
static int __init test_kobj_init(void)
{
 
    INIT_WORK(&work1,do_work1);
    INIT_WORK(&work2,do_work2);
 
    struct task_struct *  thread =  kthread_create( threadfn,(void * )&data,"mythread");
    if(thread != NULL)
    {
        printk(KERN_INFO "thread create success");
        wake_up_process(thread);
        
    }else
    {
        printk(KERN_ERR "thread create err");
    }
 
     return 0;
}
 
 
 
static void __exit test_kobj_exit(void)
{
     printk(KERN_INFO "test_kobj_exit ");
     return;
}
 
module_init(test_kobj_init);
module_exit(test_kobj_exit);
 
MODULE_AUTHOR("copy other auther");
MODULE_LICENSE("GPL");

2、自定义工作队列举例(demol来源于网络)

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/kobject.h>
#include <linux/list.h>
#include <linux/kthread.h>
#include <asm/ptrace.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
int data  = 10;

static struct workqueue_struct *workqueue;
static struct work_struct       work1;
static struct work_struct       work2;



static void do_work1(struct work_struct *arg)
{
    printk(KERN_INFO "do_work1 .....");
}

static void do_work2(struct work_struct *arg)
{
    printk(KERN_INFO "do_work2 .....");
}


int threadfn(void *data)
{
    static int count =  0 ;
    int args = *(int *)data;
    printk(KERN_INFO "enter thead_fn");
    while(1)
    {
        msleep(2*1000);
        printk(KERN_INFO "threadfn data: %d, count: %d",args , ++count);
        queue_work(workqueue,&work1);//将任务放到自己创建的工作队列上去执行
        queue_work(workqueue,&work2);
        
    }
} 


static int __init test_kobj_init(void)
{

    
   workqueue = create_workqueue("create_new_work_queue");
   INIT_WORK(&work1,do_work1);
   INIT_WORK(&work2,do_work2);

   struct task_struct *  thread =  kthread_create( threadfn,(void * )&data,"mythread");
   if(thread != NULL)
   {
       printk(KERN_INFO "thread create success");
       wake_up_process(thread);
       
   }else
   {
       printk(KERN_ERR "thread create err");
   }

    return 0;
}



static void __exit test_kobj_exit(void)
{
    printk(KERN_INFO "test_kobj_exit ");
    destroy_workqueue(workqueue);
    return;
}

module_init(test_kobj_init);
module_exit(test_kobj_exit);

MODULE_AUTHOR("copy other auther");
MODULE_LICENSE("GPL");

在使用如下函数时注意事项


    1、flush_work():堵塞工作任务,直到工作任务完成

    2、flush_delayed_work():等待延时工作任务完成

    3、cancel_work_sync():取消工作任务并等待它完成

    4、cancel_delayed_work():取消延时工作任务

    5、cancel_delayed_work_sync():取消延时工作任务并等待它完成

    6、create_workqueue():对于多CPU系统,内核会在每个CPU上创建一个工作队列,使线程处理并行化

    7、create_singlethread_workqueue():内核只在一个CPU上创建一个工作队列

    8、queue_work_on():在指定CPU上添加工作任务,queue_work()调用queue_work_on()在所有CPU上添加工作任务

常见的应用场景:


work_struct 是 Linux 内核中用于处理异步任务的机制,它允许内核将任务推迟到工作队列中,由工作线程在适当的时候执行。work_struct 可以处理多种类型的任务,以下是一些常见的应用场景:
1. 中断处理
中断处理程序需要快速完成,以避免过长时间占用 CPU。如果中断处理程序需要执行一些耗时的操作,可以将这些操作推迟到工作队列中执行。例如:



static void my_interrupt_handler(int irq, void *dev_id) {
    struct work_struct *work = (struct work_struct *)dev_id;
    queue_work(my_workqueue, work);
}

2. 定时任务
如果需要在特定时间后执行某些任务,可以使用 queue_delayed_work 将任务推迟到工作队列中。例如:



static void my_delayed_work_handler(struct work_struct *work) {
    printk(KERN_INFO "Delayed work handler called\n");
}

static int __init my_module_init(void) {
    my_workqueue = create_workqueue("my_workqueue");
    if (!my_workqueue) {
        return -ENOMEM;
    }

    struct my_work *my_work = kmalloc(sizeof(struct my_work), GFP_KERNEL);
    if (!my_work) {
        return -ENOMEM;
    }

    INIT_DELAYED_WORK(&my_work->work, my_delayed_work_handler);
    queue_delayed_work(my_workqueue, &my_work->work, msecs_to_jiffies(1000)); // 延迟 1 秒

    return 0;
}


3. 文件系统操作
在文件系统操作中,某些耗时的操作可以推迟到工作队列中执行。例如,文件系统的同步操作可以使用工作队列来处理:


static void my_sync_handler(struct work_struct *work) {
    printk(KERN_INFO "File system sync handler called\n");
    // 执行文件系统同步操作
}

static int __init my_module_init(void) {
    my_workqueue = create_workqueue("my_workqueue");
    if (!my_workqueue) {
        return -ENOMEM;
    }

    struct my_work *my_work = kmalloc(sizeof(struct my_work), GFP_KERNEL);
    if (!my_work) {
        return -ENOMEM;
    }

    INIT_WORK(&my_work->work, my_sync_handler);
    queue_work(my_workqueue, &my_work->work);

    return 0;
}

4. 设备驱动程序
在设备驱动程序中,某些操作(如硬件初始化、数据传输等)可以推迟到工作队列中执行。例如:



static void my_device_init_handler(struct work_struct *work) {
    printk(KERN_INFO "Device initialization handler called\n");
    // 执行设备初始化操作
}

static int __init my_module_init(void) {
    my_workqueue = create_workqueue("my_workqueue");
    if (!my_workqueue) {
        return -ENOMEM;
    }

    struct my_work *my_work = kmalloc(sizeof(struct my_work), GFP_KERNEL);
    if (!my_work) {
        return -ENOMEM;
    }

    INIT_WORK(&my_work->work, my_device_init_handler);
    queue_work(my_workqueue, &my_work->work);

    return 0;
}

5. 网络协议栈
在网络协议栈中,某些操作(如数据包处理、连接管理等)可以推迟到工作队列中执行。例如:



static void my_packet_handler(struct work_struct *work) {
    printk(KERN_INFO "Packet handler called\n");
    // 处理数据包
}

static int __init my_module_init(void) {
    my_workqueue = create_workqueue("my_workqueue");
    if (!my_workqueue) {
        return -ENOMEM;
    }

    struct my_work *my_work = kmalloc(sizeof(struct my_work), GFP_KERNEL);
    if (!my_work) {
        return -ENOMEM;
    }

    INIT_WORK(&my_work->work, my_packet_handler);
    queue_work(my_workqueue, &my_work->work);

    return 0;
}

6. 用户空间请求
在处理用户空间请求时,某些操作可以推迟到工作队列中执行。例如:



static void my_user_request_handler(struct work_struct *work) {
    printk(KERN_INFO "User request handler called\n");
    // 处理用户请求
}

static int __init my_module_init(void) {
    my_workqueue = create_workqueue("my_workqueue");
    if (!my_workqueue) {
        return -ENOMEM;
    }

    struct my_work *my_work = kmalloc(sizeof(struct my_work), GFP_KERNEL);
    if (!my_work) {
        return -ENOMEM;
    }

    INIT_WORK(&my_work->work, my_user_request_handler);
    queue_work(my_workqueue, &my_work->work);

    return 0;
}

注意事项

    工作函数的执行环境:工作函数在工作线程中执行,允许睡眠,因此可以执行耗时操作。
    工作队列的销毁:在模块退出时,确保使用 flush_workqueue 等待所有工作完成,然后使用 destroy_workqueue 销毁工作队列。
    工作项的释放:在工作函数中,确保释放分配的工作项,避免内存泄漏。

通过这些示例,你可以看到 work_struct 可以处理多种类型的任务,包括中断处理、定时任务、文件系统操作、设备驱动程序、网络协议栈和用户空间请求等。这使得 work_struct 成为内核中处理异步任务的强大工具。


http://www.kler.cn/a/538845.html

相关文章:

  • 每日Attention学习22——Inverted Residual RWKV
  • SpringSecurity:授权服务器与客户端应用(入门案例)
  • 在 Visual Studio Code 与微信开发者工具中调试使用 emscripten 基于 C 生成的 WASM 代码
  • unity碰撞的监测和监听
  • 28.<Spring博客系统⑤(部署的整个过程(CentOS))>
  • 文件上传到腾讯云存储、签名及设置过期时间
  • (done) openMP学习 (Day9: 优化链表操作)
  • DeepSeek介绍,以及本地部署和API使用
  • 内容中台赋能人工智能技术提升业务创新能力
  • Jenkins 使用教程:从入门到精通
  • 如何使用JLINK连接雅特力MCU
  • Ada语言的云计算
  • LeetCode 力扣热题100 将有序数组转换为二叉搜索树
  • fps动作系统7:武器摇摆
  • 人工智能应用实例-自动驾驶A*算法高级应用
  • LS-SDMTSP:粒子群优化算法(PSO)求解大规模单仓库多旅行商问题(LS-SDMTSP),MATLAB代码
  • 写综述小论文的反思
  • 2.9寒假作业
  • 将DeepSeek接入Excel实现交互式对话
  • springboot集成日志
  • Meta AI 最近推出了一款全新的机器学习框架ParetoQ,专门用于大型语言模型的4-bit 以下量化
  • 在Ubuntu云服务器上使用OneFormer模型进行遥感图像水体提取,并替换为客户数据集的详细步骤
  • 战场物联网中的移动雾人工智能
  • Docker、Kubernetes (k8s) 和 Docker Compose 的概念
  • 活动预告 | 为 AI 新纪元做好准备:助力安全的业务转型
  • 律所录音证据归集工具:基于PyQt6与多线程的自动化音频管理解决方案