linux work queue工作队列小结与使用

本文介绍了Linux内核中的工作队列(workqueue)机制,包括何时使用工作队列而非tasklet,以及如何利用API创建、调度和取消任务。通过具体实例展示了如何实现周期性任务和延时执行。

1、linux内核中断处理的工作队列workqueue机制

工作队列(workqueue)是另外一种将工作 推后执行的形式。工作队列可以把工作推后,交由一个内核线程去执行,也就是说,这个下半部分可以在进程上下文中执行。 这样,通过工作队列执行的代码能占尽进程上下文的所有优势。最重要的就是工作队列允许被重新调度甚至是睡眠。
那么,什么情况下使用工作队列,什么情况下使用tasklet。如果推后执行的任务需要睡眠,那么就选择工作队列。如果推后执行的任务不需要睡眠,那么就选择tasklet。另外,如果需要用一个可以重新调度的实体来执行你的下半部处理,也应该使用工作队列。它是唯一能在进程上下文运行的下半部实现的机制,也只有它才可以睡眠。这意味着在需要获得大量的内存时、在需要获取信号量时,在需要执行阻塞式的I/O操作时,它都会非常有用。如果不需要用一个内核线程来推后执行工作,那么就考虑使用tasklet。

2、work queue API

 

1、create_workqueue用于创建一个workqueue队列,为系统中的每个CPU都创建一个内核线程。输入参数:

@name:workqueue的名称

2create_singlethread_workqueue用于创建workqueue,只创建一个内核线程。输入参数:

@name:workqueue名称

3destroy_workqueue释放workqueue队列。输入参数:

@ workqueue_struct:需要释放的workqueue队列指针

4schedule_work调度执行一个具体的任务,执行的任务将会被挂入Linux系统提供的workqueue——keventd_wq输入参数:

@ work_struct:具体任务对象指针

5schedule_delayed_work延迟一定时间去执行一个具体的任务,功能与schedule_work类似,多了一个延迟时间,输入参数:

@work_struct:具体任务对象指针

@delay:延迟时间

6queue_work调度执行一个指定workqueue中的任务。输入参数:

@ workqueue_struct:指定的workqueue指针

@work_struct:具体任务对象指针

7queue_delayed_work延迟调度执行一个指定workqueue中的任务,功能与queue_work类似,输入参数多了一个delay。

 

3、实例分析 为我所用

//声明

static struct workqueue_struct *mdp_pipe_ctrl_wq; /* mdp mdp pipe ctrl wq */

static struct delayed_work mdp_pipe_ctrl_worker;

//driver int时创建 工作队列

mdp_pipe_ctrl_wq = create_singlethread_workqueue("mdp_pipe_ctrl_wq");//创建工作队列

INIT_DELAYED_WORK(&mdp_pipe_ctrl_worker,mdp_pipe_ctrl_workqueue_handler);/

/delayed_work与task_func绑定。

//处理函数

static void mdp_pipe_ctrl_workqueue_handler(struct work_struct *work)
{
 mdp_pipe_ctrl(MDP_MASTER_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
}

//开始调用工作队列,delay时间到了就执行处理函数。

unsigned long mdp_timer_duration = (HZ/20);   /* 50 msecond */

/* send workqueue to turn off mdp power */

queue_delayed_work(mdp_pipe_ctrl_wq,&mdp_pipe_ctrl_worker, mdp_timer_duration);

/* cancel pipe ctrl worker */

cancel_delayed_work(&mdp_pipe_ctrl_worker);

/* for workder can't be cancelled... */

flush_workqueue(mdp_pipe_ctrl_wq);

/* for workder can't be cancelled... */

flush_workqueue(mdp_pipe_ctrl_wq);

driver 程序中许多很多情况需要设置延后执行的,这样工作队列就很好帮助我们实现。

还有一个正要特性就是循环调度的功能,一般电池中比较常见。

如:

INIT_DELAYED_WORK(&di->monitor_work, ds2760_battery_work);

 

static void ds2760_battery_work(struct work_struct *work)
{
 struct ds2760_device_info *di = container_of(work,
  struct ds2760_device_info, monitor_work.work);
 const int interval = HZ * 60;

 dev_dbg(di->dev, "%s\n", __func__);

 ds2760_battery_update_status(di);
 queue_delayed_work(di->monitor_wqueue, &di->monitor_work, interval);//工作队列每隔60s重新调度该任务函数。
}

 


<think>我们正在讨论Linux内核中的工作队列workqueue)的使用方法。根据引用[1]和[2]的内容,我们可以总结如下:1.工作队列是一种将工作推后执行的机制,它允许重新调度甚至睡眠(因为工作是在内核线程中执行的)。2.使用工作队列的基本步骤包括:初始化工作任务work),将工作任务挂入工作队列,以及可能需要的取消和刷新操作。具体使用方法:###1.定义并初始化工作任务Linux内核中,工作任务由`structwork_struct`表示。我们可以使用`INIT_WORK`宏来初始化一个工作任务。```c#include<linux/workqueue.h>structwork_structmy_work;//通常会在模块初始化函数中初始化staticint__initmy_module_init(void){INIT_WORK(&my_work,my_work_handler);//my_work_handler是处理函数return0;}```其中,`my_work_handler`是工作处理函数,其函数签名为:```cvoidmy_work_handler(structwork_struct*work){//处理工作}```###2.调度工作任务初始化后,可以将工作任务调度到工作队列中。有两种方式:-调度到系统默认的工作队列(system_wq):使用`schedule_work`或`schedule_work_on`(指定CPU)。-调度到自定义的工作队列使用`queue_work`或`queue_work_on`(指定CPU)。####使用系统默认工作队列```cschedule_work(&my_work);//调度到默认工作队列schedule_work_on(cpu,&my_work);//指定CPU```####使用自定义工作队列首先需要创建自己的工作队列:```cstructworkqueue_struct*my_wq=alloc_workqueue("my_wq",flags,max_active);```其中,`flags`是创建标志(如`WQ_MEM_RECLAIM`等),`max_active`是最大活动任务数。然后,将工作任务加入自定义工作队列:```cqueue_work(my_wq,&my_work);queue_work_on(cpu,my_wq,&my_work);```###3.取消和刷新工作任务如果需要取消一个已经调度但尚未执行工作任务,可以使用`cancel_work_sync`(同步取消,会等待任务完成):```ccancel_work_sync(&my_work);```如果要刷新(等待完成)系统默认工作队列中的所有任务使用:```cflush_scheduled_work();```对于自定义工作队列使用:```cflush_workqueue(my_wq);```###4.销毁工作队列在模块卸载时,需要销毁自定义工作队列:```cdestroy_workqueue(my_wq);```###注意事项-使用系统默认工作队列时,不需要自己创建和销毁工作队列,但要注意默认工作队列是共享的,长时间运行的任务可能会影响系统性能。-对于需要长时间运行或需要特定调度要求的工作,建议创建专用工作队列。###示例代码下面是一个简单的示例,展示如何使用系统默认工作队列:```c#include<linux/init.h>#include<linux/module.h>#include<linux/workqueue.h>staticstructwork_structmy_work;voidmy_work_handler(structwork_struct*work){printk(KERN_INFO"Workhandlerisrunning\n");}staticint__initmy_init(void){INIT_WORK(&my_work,my_work_handler);schedule_work(&my_work);printk(KERN_INFO"Workscheduled\n");return0;}staticvoid__exitmy_exit(void){//确保工作完成flush_scheduled_work();printk(KERN_INFO"Moduleexit\n");}module_init(my_init);module_exit(my_exit);```§§相关问题§§1.工作队列workqueue)和任务队列(tasklet)有什么区别?2.如何创建工作队列并设置其属性(如并发级别、优先级等)?3.工作队列中的任务是否可以睡眠?为什么?4.在什么情况下应该使用自定义工作队列而不是系统默认的工作队列?</think>### Linux内核工作队列workqueue)的使用方法 工作队列workqueue)是Linux内核中用于异步执行任务的机制,特别适合需要睡眠或重新调度的场景[^1]。以下是详细使用步骤: #### 1. **定义工作任务** 使用`struct work_struct`表示工作任务,并通过`INIT_WORK()`宏初始化: ```c #include <linux/workqueue.h> struct work_struct my_work; // 初始化工作任务(静态方式) static DECLARE_WORK(my_work, work_handler); // 或动态初始化(在模块初始化函数中) INIT_WORK(&my_work, work_handler); ``` 其中`work_handler`是任务处理函数: ```c void work_handler(struct work_struct *work) { printk(KERN_INFO "Work executed\n"); // 可在此处执行需要睡眠的操作 } ``` #### 2. **调度工作任务** - **默认工作队列(system_wq)** 使用内核预定义的全局队列: ```c schedule_work(&my_work); // 调度到当前CPU schedule_work_on(cpu_id, &my_work); // 指定目标CPU[^2] ``` - **自定义工作队列** 创建专用队列(适用于高频任务): ```c struct workqueue_struct *my_wq = alloc_workqueue("my_queue", WQ_MEM_RECLAIM, 1); queue_work(my_wq, &my_work); // 提交任务 ``` #### 3. **任务管理** - **取消任务** 同步取消未执行任务: ```c cancel_work_sync(&my_work); // 等待任务完成[^2] ``` - **刷新队列** 确保所有任务完成: ```c flush_workqueue(my_wq); // 自定义队列 flush_scheduled_work(); // 默认队列[^2] ``` #### 4. **销毁资源** 卸载模块时释放资源: ```c destroy_workqueue(my_wq); // 销毁自定义队列 ``` ### 关键特性 1. **睡眠支持** 工作队列在进程上下文中运行,允许调用`sleep()`等可能阻塞的函数[^1]。 2. **并发控制** 通过`alloc_workqueue()`的`max_active`参数控制并发任务数。 3. **CPU绑定** 使用`schedule_work_on()`或`queue_work_on()`指定目标CPU[^2]。 ### 典型应用场景 - 中断下半部处理(需睡眠时) - 异步文件操作 - 定时任务执行 - 驱动程序的延迟初始化 > **注意**:对实时性要求高的场景应优先选择Tasklet或SoftIRQ,因工作队列涉及线程调度开销。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值