linux INIT_WORK 创建工作队列

本文详细介绍了 Linux 2.6 内核中的工作队列机制,包括如何使用系统共享的工作队列及如何创建自定义工作队列。探讨了工作队列与 tasklet 的区别,以及在不同场景下的应用方式。
Linux 2.6内核使用了不少工作队列来处理任务,他在使用上和 tasklet最大的不同是工作队列的函数可以使用休眠,而tasklet的函数是不允许使用休眠的。

工作队列的使用又分两种情况,一种是利用系统共享的工作队列来添加自己的工作,这种情况处理函数不能消耗太多时间,这样会影响共享队列中其他任务的处理;另外一种是创建自己的工作队列并添加工作。

(一)利用系统共享的工作队列添加工作

Ø        第一步:声明或编写一个工作处理函数

void my_func();

Ø        第二步:创建一个工作结构体变量,并将处理函数和参数的入口地址赋给这个工作结构体变量

DECLARE_WORK(my_work,my_func,&data); //编译时创建名为my_work的结构体变量并把函数入口地址和参数地址赋给它;

如果不想要在编译时就用DECLARE_WORK()创建并初始化工作结构体变量,也可以在程序运行时再用INIT_WORK()创建

struct work_struct my_work; //创建一个名为my_work的结构体变量,创建后才能使用INIT_WORK()

INIT_WORK(&my_work,my_func,&data); //初始化已经创建的my_work,其实就是往这个结构体变量中添加处理函数的入口地址和data的地址,通常在驱动的open函数中完成

Ø        第三步:将工作结构体变量添加入系统的共享工作队列

schedule_work(&my_work); //添加入队列的工作完成后会自动从队列中删除

schedule_delayed_work(&my_work,tick); //延时tick个滴答后再提交工作

(二)创建自己的工作队列来添加工作

Ø        第一步:声明工作处理函数和一个指向工作队列的指针

void my_func();

struct workqueue_struct *p_queue;

Ø        第二步:创建自己的工作队列和工作结构体变量(通常在open函数中完成)

p_queue=create_workqueue("my_queue"); //创建一个名为my_queue的工作队列并把工作队列的入口地址赋给声明的指针

struct work_struct my_work;

INIT_WORK(&my_work, my_func, &data); //创建一个工作结构体变量并初始化,和第一种情况的方法一样

Ø        第三步:将工作添加入自己创建的工作队列等待执行

queue_work(p_queue, &my_work);

//作用与schedule_work()类似,不同的是将工作添加入p_queue指针指向的工作队列而不是系统共享的工作队列

Ø        第四步:删除自己的工作队列

destroy_workqueue(p_queue); //一般是在close函数中删除
在T2上创建队列 ,延时几个MS才响应,这是什么概念!还有解决这问题!!

 

1.INIT_WORK(struct work_struct *work, void (*function)(void *), void *data)
上面一句只是定义了work和work对应的操作。
要是在实际使用的时候还是需要你去在适当的条件下激活这个work。只有激活了这个work,
这个work才有运行的机会。这个激活操作接口是shudule_work或是queue_work。
这两个接口之后只是说这个work有了运行的机会,但是具体到什么时候运行,那要看你用哪个接口激活的。
如果是shudule_work的话,系统中有个events内核线程,这个线程会处理你用shudule_work接口激活
的所有work。如果是queue_work的话,一般这种情况都是自己创建了一个单独的处理线程,这样将
你激活的work和这个线程联系起来。至于什么时候运行,那就是events或是你定义的特定线程运行的时候。

2.至于你提到的为什么要用到work。这个的话,我个人的理解是:一般用在对耗时处理上。比如,
当中断发生的时候,你可以在中断上下文中完成激活操作,让那些耗时的操作在work中完成。

 

在2.6.22版本中,INIT_WORK已经做了大幅度的修改。INIT_WORK现在使用2个参数,分别是链表和任务,去掉了数据。这时我们的任务func以_work作为参数。我们需要将我们的work结构加入到我们的data结构中,然后使用container_of这个函数来求出我们的data的指针。

 

 

<think>我们正在讨论Linux内核中的INIT_WORK宏。根据提供的引用,INIT_WORK是用于初始化一个工作任务work_struct)的宏,它将一个具体的任务单元绑定到一个回调函数上,以便稍后通过工作队列机制调度执行。首先,让我们明确INIT_WORK的作用:1.初始化一个工作结构(work_struct),将其与一个工作函数绑定。2.这样,当这个工作被调度到工作队列上时,绑定的函数就会被执行。使用INIT_WORK的步骤通常包括:1.声明一个work_struct结构体变量(可以使用DECLARE_WORK在编译时声明并初始化,也可以在运行时声明并用INIT_WORK初始化)。2.编写一个工作函数,该函数的原型为:voidmy_work_function(structwork_struct*work);3.在驱动初始化函数(如module_init对应的函数)中,使用INIT_WORK宏将工作结构和工作函数绑定。4.在需要调度工作执行时,使用schedule_work()将工作提交到系统默认的工作队列,或者使用queue_work()提交到自定义工作队列。注意:引用[4]提到INIT_WORK可以传递参数,但实际上,INIT_WORK宏的原型是:```cINIT_WORK(_work,_func)```其中,_work是指向work_struct的指针,_func是工作函数。在旧版本的内核中,有一个三个参数的版本(第三个参数是传递给函数的参数),但新版本已经不再使用。现在,如果需要传递参数,通常通过container_of宏从work_struct指针获取包含它的结构体指针。例如,假设我们有一个自定义结构体,其中包含了work_struct:```cstructmy_data{intvalue;structwork_structmy_work;};```然后,工作函数可以这样写:```cvoidmy_work_function(structwork_struct*work){structmy_data*md=container_of(work,structmy_data,my_work);//使用md->value}```初始化时:```cstructmy_datadata;INIT_WORK(&data.my_work,my_work_function);```调度工作时:```cschedule_work(&data.my_work);```引用[2]和[3]提供了类似的示例。引用[5]则展示了延时工作队列的使用(INIT_DELAYED_WORK)。因此,总结INIT_WORK的使用方法:1.定义工作结构体(work_struct)并编写工作函数。2.使用INIT_WORK初始化这个工作结构体,指定工作函数。3.在适当的时机(例如中断处理函数、定时器等)调度工作。注意事项:-工作函数在进程上下文中运行,因此可以睡眠(但需要注意,长时间睡眠可能会影响工作队列中其他任务的执行)。-工作队列是异步执行机制,调度工作后,函数不会立即执行,而是由内核线程在某个时间点执行。-如果工作已经被调度但尚未执行,再次调度同一个工作可能会导致它被执行两次(除非使用取消工作函数先取消)。因此,需要确保工作不会在运行中被重新调度,除非明确需要。现在,根据用户的问题,我们来组织回答。</think>### Linux内核中`INIT_WORK`宏的作用及使用方法 #### 一、作用解析 `INIT_WORK`是Linux内核用于初始化**工作任务结构**(`struct work_struct`)的关键宏,其核心功能包括: 1. **任务绑定** 将工作结构体与具体的回调函数关联,当任务被调度时自动执行该函数[^1]。 2. **异步执行基础** 为工作队列机制提供基础单元,实现中断上下文与进程上下文的解耦[^2]。 3. **线程安全初始化** 原子化设置工作结构体的状态标志,确保安全加入工作队列[^1]。 #### 二、使用方法(三步流程) ##### 步骤1:声明工作结构体 ```c // 静态声明(编译时初始化) DECLARE_WORK(my_work_name, my_work_function); // 或动态声明(运行时初始化) struct work_struct my_work; ``` ##### 步骤2:编写工作函数 ```c void my_work_function(struct work_struct *work) { // 通过container_of获取包含结构体(参数传递关键) struct my_device *dev = container_of(work, struct my_device, work); printk("Value: %d\n", dev->data); } ``` ##### 步骤3:初始化并调度 ```c static int __init my_init(void) { // 初始化绑定(核心步骤) INIT_WORK(&my_work, my_work_function); // 提交到系统默认工作队列 schedule_work(&my_work); // 或提交到自定义工作队列 struct workqueue_struct *wq = create_workqueue("my_wq"); queue_work(wq, &my_work); return 0; } ``` #### 三、关键注意事项 1. **参数传递机制** 需通过`container_of`从`work_struct`反推包含结构体地址,而非直接传参[^3][^4]: ```c struct my_data { int param; struct work_struct work; }; // 在工作函数中获取参数: struct my_data *d = container_of(work, struct my_data, work); ``` 2. **执行上下文** 工作函数在**进程上下文**运行,允许休眠(如`msleep()`),但禁止访问用户空间[^1]。 3. **重复调度保护** 同一工作结构体若已在队列中,再次调度会被忽略(需先调用`cancel_work_sync()`)[^2]。 #### 四、典型应用场景 1. **中断下半部处理** 在中断处理函数中调度耗时操作: ```c irq_handler_t irq_handler(...) { schedule_work(&my_work); // 避免阻塞中断 return IRQ_HANDLED; } ``` 2. **延迟任务执行** 结合`INIT_DELAYED_WORK`实现延时调度[^5]: ```c struct delayed_work dwork; INIT_DELAYED_WORK(&dwork, delayed_func); schedule_delayed_work(&dwork, HZ); // 延时1秒执行 ``` #### 五、与相关接口对比 | **接口** | **特点** | |----------------------|----------------------------------| | `INIT_WORK` | 立即任务初始化 | | `INIT_DELAYED_WORK` | 支持延时调度[^5] | | `schedule_work()` | 提交到系统全局队列`events` | | `queue_work()` | 提交到自定义工作队列 | > **提示**:内核 ≥5.9 版本后推荐使用`INIT_WORK()`替代过时的`INIT_WORK(_work, _func, _data)`三参数形式[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值