INIT_WORK的理解

#define INIT_WORK(_work, _func, _data) \
do { \
INIT_LIST_HEAD(&(_work)->entry); \
(_work)->pending = 0; \
PREPARE_WORK((_work), (_func), (_data)); \
init_timer(&(_work)->timer); \
} while (0)

#define PREPARE_WORK(_work, _func, _data) \
do { \
(_work)->func = _func; \
(_work)->data = _data; \
} while (0)

INIT_WORK会在你定义的_work工作队列里面增加一个工作任务,该任务就是_func。_func这个任务会需要一些数据作为参数,这个参数就是通过_data传递的。
任务的启动是通过调度程序schedule_work来处理的。
INIT_LIST_HEAD是初始化一个链表,就是在此之前,链表是不存在的。
(_work)->pending是调度程序需要使用的一个标志,没有仔细研究用处。
PREPARE_WORK就是进行赋值,确定链表中这个对象的任务和数据。
init_timer是初始化这个对象的任务时间,具体作用没有研究。

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

//include/linux/workqueue.h
#define INIT_WORK(_work, _func)                                         \
        __INIT_WORK((_work), (_func), 0)

#ifdef CONFIG_LOCKDEP
#define __INIT_WORK(_work, _func, _onstack)                             \
        do {                                                            \
                static struct lock_class_key __key;                     \
                                                                        \
                __init_work((_work), _onstack);                         \
                (_work)->data = (atomic_long_t) WORK_DATA_INIT();       \
                lockdep_init_map(&(_work)->lockdep_map, #_work, &__key, 0); \
                INIT_LIST_HEAD(&(_work)->entry);                        \
                (_work)->func = (_func);                                \
        } while (0)
#else
#define __INIT_WORK(_work, _func, _onstack)                             \
        do {                                                            \
                __init_work((_work), _onstack);                         \
                (_work)->data = (atomic_long_t) WORK_DATA_INIT();       \
                INIT_LIST_HEAD(&(_work)->entry);                        \
                (_work)->func = (_func);                                \
        } while (0)
#endif

老版本内核:

以前内核里对这个函数是这样定义的 #define INIT_WORK(_work, _func, _data),可以理解为INIT_WORK会在你定义的_work工作队列里面增加一个工作任务,该任务就是_func。_func这个任务会需要一些数据作为参数,这个参数就是通过_data传递的
而现在看驱动的时候你会发现调用INIT_WORK的时候是只有两个参数,去掉了数据的部分。也许你会问怎么传递data呢,等下会讲述到。其实现在我对这个理解还是比较模糊,希望各位说说你们的理解,顺便也让我理解更透彻一点。看许多驱动模块的时候会发觉work就是一个工作队列,一般是结构体work_struct,主要的目的就是用来处理中断的。比如在中断里面要做很多事,但是比较耗时,这时就可以把耗时的工作放到工作队列。说白了就是系统延时调度的一个自定义函数
现在已goodix的触摸屏列举一下步骤:: ^
   1. 在探测函数里goodix_ts_probe初始化
INIT_WORK(&ts->work, goodix_ts_work_func);//struct work_struct  work,ts是client私有数据结构体
主要目的就是因为就算没有中断,在第一次开机时也有检测设备,所以这个任务一开始就执行,只不过到后来中断发生后就再执行.
2. 现在当然轮到static void goodix_ts_work_func(struct work_struct *work)这个工作任务了
在这个函数里会有一句:
struct goodix_ts_data *ts = container_of(work, struct goodix_ts_data, work);
这函数的主要目的就是解决前面所说的我们的data跑哪去了,使用container_of这个函数来求出我们的data的指针
3.当然你不能忘记注册你的中断函数了
request_irq(client->irq, goodix_ts_irq_handler,pdata->irq_edge ? IRQ_TYPE_EDGE_FALLING : IRQ_TYPE_EDGE_RISING,client->name, ts);
所以用一个结构体ts就可以把中断,任务,设备名等全部囊括进来

https://blog.youkuaiyun.com/bingqingsuimeng/article/details/7891157讲的很好

https://blog.youkuaiyun.com/fcf1990501/article/details/8445441  https://blog.youkuaiyun.com/lhf_tiger/article/details/7109184

 

 

 

<think>好的,用户现在问的是关于`cancel_work_sync`的使用方法。之前他们问了在hrtimer中断里调用queue_work的问题,现在转向了如何正确取消工作项。我需要先回顾之前的对话,确保回答一致。 首先,用户可能正在开发内核模块,涉及工作队列和定时器。他们在处理模块卸载或资源清理时需要确保没有未完成的工作项,避免内存泄漏或崩溃。所以用户需要了解如何正确使用cancel_work_sync来安全地取消工作。 接下来,我需要确认`cancel_work_sync`的用法。这个函数的作用是取消一个已排队的工作项,并等待其完成。使用时需要注意以下几点:确保工作队列已经初始化,不能在中断上下文中调用,以及避免死锁情况。 然后,用户可能需要具体的代码示例。我应该提供一个简单的示例,展示如何初始化和清理工作项,包括在模块退出时调用`cancel_work_sync`。同时,要强调与工作队列销毁的顺序,先取消工作项再销毁队列。 另外,用户可能遇到的问题包括模块卸载时的竞态条件,或者多次调用`cancel_work_sync`导致的问题。需要提醒他们确保工作项只被取消一次,并处理可能的重入情况。 还有,用户可能对`cancel_work_sync`和`flush_work`的区别不太清楚。需要简要说明两者的不同,`cancel_work_sync`会尝试取消,而`flush_work`只是等待完成。 最后,总结使用时需要注意的关键点,比如上下文要求、生命周期管理和错误处理。确保用户明白在进程上下文中调用,并且正确处理返回值,检查是否成功取消。 需要确保回答结构清晰,步骤明确,代码示例正确,并且覆盖常见问题。同时,使用中文,避免技术术语过于晦涩,让用户容易理解。</think>在Linux内核中,`cancel_work_sync()` 是一个关键函数,用于**安全取消已提交到工作队列(workqueue)中的任务**(`work_struct`),并确保该任务不再执行或等待其完成。以下是其核心用法和注意事项: --- ### **1. 函数原型** ```c bool cancel_work_sync(struct work_struct *work); ``` - **参数**:`work` 指向需要取消的 `work_struct` 对象。 - **返回值**: - `true`:成功取消尚未开始执行的任务。 - `false`:任务已在运行或已完成。 --- ### **2. 典型使用场景** 当需要确保某个工作项(`work_struct`)在模块卸载、资源释放或状态重置时**不再执行或等待其完成**,例如: - 模块退出时清理残留任务。 - 避免工作项访问已释放的内存。 - 防止任务因竞态条件重复触发。 --- ### **3. 使用步骤** #### **(1) 初始化工作项** ```c // 定义工作项和处理函数 static struct work_struct my_work; void my_work_handler(struct work_struct *work) { printk(KERN_INFO "Work executed\n"); } // 模块初始化时调用 static int __init my_module_init(void) { INIT_WORK(&my_work, my_work_handler); // 绑定工作项与处理函数 queue_work(system_wq, &my_work); // 提交到系统默认工作队列 return 0; } ``` #### **(2) 安全取消工作项** ```c // 模块卸载时调用 static void __exit my_module_exit(void) { bool ret = cancel_work_sync(&my_work); // 尝试取消工作项 if (ret) printk(KERN_INFO "Work cancelled before execution\n"); else printk(KERN_INFO "Work already running or completed\n"); } ``` --- ### **4. 关键注意事项** #### **(1) 调用上下文要求** - **禁止在中断上下文中调用**:`cancel_work_sync()` **可能休眠等待任务完成**,因此必须确保在**进程上下文**(如模块卸载函数)中调用。 - **避免死锁**:若工作项的处理函数试图获取与调用者相同的锁,会导致死锁。 #### **(2) 工作队列生命周期管理** - **销毁工作队列前必须取消所有工作项**: 如果工作项提交到自定义工作队列(非系统默认队列),需先调用 `cancel_work_sync()`,再销毁队列: ```c destroy_workqueue(my_wq); // 必须先取消所有工作项! ``` #### **(3) 多次调用风险** - **幂等性**:`cancel_work_sync()` 可以多次调用,但需确保工作项未被重新提交(例如在取消后再次调用 `queue_work()`)。 --- ### **5. 常见问题** #### **(1) 模块卸载时崩溃** - **原因**:未调用 `cancel_work_sync()`,导致工作项在模块卸载后访问已释放的内存。 - **解决**:在模块退出函数中强制取消所有工作项。 #### **(2) 任务无法取消** - **原因**:工作项已经运行且处理函数未退出(例如因阻塞操作卡住)。 - **解决**:确保工作处理函数能及时完成,或使用超时机制(如 `delayed_work`)。 --- ### **6. 替代函数对比** | 函数 | 行为 | |---------------------|----------------------------------------------------------------------| | `cancel_work_sync()` | 取消未执行的任务,若任务已运行则等待完成。 | | `flush_work()` | 不尝试取消任务,仅等待任务完成(即使任务尚未开始)。 | --- ### **总结** - **何时使用**:在需要**确保工作项不再运行或等待其完成**时(如模块卸载)。 - **正确顺序**:销毁工作队列前,先调用 `cancel_work_sync()`。 - **错误处理**:检查返回值以区分任务状态,避免资源泄漏。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值