Linux驱动中delayed_workqueue使用

本文详细解释了Linux驱动中delayed_workqueue的使用方法,包括定义、初始化、创建线程队列及运行队列的过程。重点强调了在非中断环境下,delayed_workqueue如何发挥其功效,以及其与timer内核API的比较。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Linux驱动中delayed_workqueue使用:

在驱动中,有时不能使用中断(这种情况很少遇到), 此时delayed_workqueue就可以发挥其巨大的功效了。也可以用其它同类的内核API实现:如timer。

delayed_workqueue的使用过程如下:

    --> 定义workqueue: struct workqueue_struct *test_workqueue;                                      // 定义在: kernel/workqueue.c

    --> 定义workqueue要做的delayed工作:struct delayed_work test_delayed_work;       // 定义在: include/linux/workqueue.h

    --> 初始化workqueue:INIT_DELAYED_WORK(&test_delayed_work, test_workqueue_func);    // 定义在:include/linux/workqueue.h

    --> 创建线程queue并加以名字:test_workqueue = create_singlethread_workqueue("name_of_this_queue");

    --> 运行queue:queue_delayed_work(test_workqueue, &test_delayed_work, delay_time);  // 定义在:kernel/workqueue.c, 其中delay_time是延迟多少时间来运行queue。

注: 在test_workqueu_func中一定要再次将delayed_workqueue加入queue中,即再次运行queue_delayed_work。否则,此queue只运行一次。


下面详细讲解每个函数。

#define INIT_DELAYED_WORK(_work, _func)				\
	do {							\
		INIT_WORK(&(_work)->work, (_func));		\
		init_timer(&(_work)->timer);			\
	} while (0)
#define INIT_WORK(_work, _func)						\
	do {								\
		(_work)->data = (atomic_long_t) WORK_DATA_INIT();	\
		INIT_LIST_HEAD(&(_work)->entry);			\
		PREPARE_WORK((_work), (_func));				\
	} while (0)
#endif

// WORK_DATA_INIT() == 0;
// PREPARE_WORK((_work), (_func)) == (_work)->func = (_func);

struct delayed_work {
	struct work_struct work;
	struct timer_list timer;
};

INIT_WORK的主要工作是data赋值为0;

初始化entry链表;

将_func传给work的_func。

#define init_timer(timer) init_timer_key((timer), NULL, NULL)

init_timer_key((timer), NULL, NULL) --> debug_time_init [ NOP]


使用delayed workqueue最主要的是调用queue_delayed_work。

如果delay = 0,则直接调用:queue_work

        其具体实现过程:queue_work --> queue_work_on --> __queue_work --> insert_work

如果delay !=0,则调用:queue_delayed_work_on

        其具体实现过程:queue_delayed_work_on 此函数是通过timer来完成最后的延时工作。



<think>我们正在讨论Linux内核中的工作同步机制。用户询问如何取消“work sync”,这很可能指的是取消已经提交到工作队列(workqueue)的工作项(work item)。在Linux内核中,工作队列是一种常用的异步执行机制,允许内核线程延迟执行函数。 关键点: 1. 工作项(struct work_struct)可以通过`INIT_WORK`宏初始化,并通过`schedule_work`或`queue_work`提交到工作队列。 2. 取消工作项可以使用`cancel_work_sync`或`cancel_delayed_work_sync`(对于延迟工作项)。 注意:取消工作项是同步操作,会等待工作项执行完成(如果已经开始执行)。如果工作项尚未执行,则取消它并确保它不会运行。 步骤: 1. 初始化工作项:使用`INIT_WORK`或`INIT_DELAYED_WORK`(对于延迟工作)。 2. 提交工作项:使用`schedule_work`(提交到系统默认的工作队列)或`queue_work`(提交到特定的工作队列)。 3. 取消工作项:使用`cancel_work_sync`或`cancel_delayed_work_sync`。 重要:`cancel_work_sync`会阻塞当前进程,直到工作项完成(如果它已经在运行)。因此,要确保不会在可能等待自己的地方调用它(例如,在工作项函数内部调用取消自身),否则会导致死锁。 函数原型: ```c bool cancel_work_sync(struct work_struct *work); bool cancel_delayed_work_sync(struct delayed_work *dwork); ``` 返回值:如果工作项被成功取消(即它还没有开始执行),则返回true;如果工作项已经在执行,那么该函数会等待它完成,然后返回false(因为工作项已经执行了)。 示例代码: 假设我们有一个工作项: ```c static struct work_struct my_work; static void my_work_func(struct work_struct *work) { // 工作项处理函数 } // 初始化 INIT_WORK(&my_work, my_work_func); // 在某个地方提交工作 schedule_work(&my_work); // 当我们想要取消这个工作项时 cancel_work_sync(&my_work); ``` 对于延迟工作项: ```c static struct delayed_work my_delayed_work; static void my_delayed_func(struct work_struct *work) { // 延迟工作项处理函数 } // 初始化 INIT_DELAYED_WORK(&my_delayed_work, my_delayed_func); // 提交延迟工作,延迟2秒 queue_delayed_work(system_wq, &my_delayed_work, 2*HZ); // 取消延迟工作项 cancel_delayed_work_sync(&my_delayed_work); ``` 注意事项: - 在取消工作项时,必须确保工作项不会被重新提交(例如在取消之后再次调度),否则可能导致竞争条件。 - 在模块退出时,通常需要取消所有已提交的工作项,以避免在模块卸载后工作项函数仍然被调用。 另外,还有一种情况:如果工作项已经被调度但尚未执行,那么取消操作会将其从队列中移除并返回true。如果工作项已经在执行,则取消操作会等待其完成,然后返回false。 因此,在模块卸载函数中,我们通常会这样写: ```c void my_module_exit(void) { cancel_work_sync(&my_work); // 或者对于延迟工作:cancel_delayed_work_sync(&my_delayed_work); // 释放资源 } ``` 引用说明: 在Linux内核中,工作队列的实现涉及多个组件,包括线程池(如引用[3]中提到的)和等待队列(如引用[2]中的`__wake_up_sync_key`可能与工作项的唤醒有关)。但用户不需要了解底层细节,只需使用工作队列提供的API即可。 相关问题: 1. 如何判断工作项是否正在执行? 2. 取消工作项时出现死锁怎么办? 3. 除了工作队列,Linux内核还有哪些异步执行机制? 注意:用户的问题是关于“work sync”的取消,我们假设是指工作队列中的工作项。如果用户指的是其他同步机制(如等待队列、完成量等),则需要进一步澄清。但根据问题中的“work sync”,我们优先考虑工作队列。</think>在 Linux 内核中,取消已提交到工作队列(workqueue)的工作项(work item)需要使用专门的取消函数。以下是关键步骤和注意事项: ### 核心方法 1. **取消普通工作项** 使用 `cancel_work_sync(struct work_struct *work)`: - 同步取消:如果工作项已在执行,会等待其完成 - 安全要求:不能在中断上下文或持有锁时调用 - 返回值:`true`(成功取消)/`false`(工作项已在运行) ```c struct work_struct my_work; INIT_WORK(&my_work, my_work_handler); // 初始化工作项 // 提交工作项 schedule_work(&my_work); // 取消工作项 if (cancel_work_sync(&my_work)) { printk("Work cancelled before execution\n"); } ``` 2. **取消延迟工作项** 使用 `cancel_delayed_work_sync(struct delayed_work *dwork)`: ```c struct delayed_work my_delayed_work; INIT_DELAYED_WORK(&my_delayed_work, my_delayed_handler); // 提交延迟工作(2秒后执行) schedule_delayed_work(&my_delayed_work, HZ*2); // 取消延迟工作 cancel_delayed_work_sync(&my_delayed_work); ``` ### 关键注意事项 1. **死锁风险** 如果工作项函数内部试图重新调度自身或等待同一队列的其他工作项,`cancel_work_sync()` 可能导致死锁。此时应改用异步取消: ```c cancel_work(&my_work); // 异步取消,不等待完成 flush_workqueue(my_wq); // 确保队列清空 ``` 2. **模块卸载处理** 在驱动模块退出时,必须取消所有工作项: ```c void my_module_exit(void) { cancel_work_sync(&my_work); cancel_delayed_work_sync(&my_delayed_work); destroy_workqueue(my_wq); // 销毁私有队列 } ``` 3. **线程池关联性** 工作项由 per-CPU 线程池执行[^3],取消操作会通过内核等待队列机制[^2]通知线程池停止处理该工作项。 ### 典型应用场景 - 中断下半部处理(替代 tasklet) - 定期轮询设备状态 - 异步文件操作(如结合文件描述符[^1]的 I/O 完成通知) --- ### 相关问题 1. 如何避免 `cancel_work_sync()` 导致的死锁? 2. 工作队列(workqueue)和任务队列(tasklet)有何区别? 3. 为什么在中断上下文中不能直接调用 `cancel_work_sync()`? 4. 如何监控 Linux 内核中工作队列的运行状态? [^1]: fopen、fclose、fread 和 fwrite 是 C 标准库中的库函数(libc),而 open、close、read、write 和 lseek 是操作系统提供的系统调用接口。文件描述符 fd [^2]: void __wake_up_sync_key(struct wait_queue_head * wq_head, unsigned int mode, int nr_exclusive, void * key) [^3]: 在系统初始化的时候,per cpu workqueue 共享的那些线程池(2 x cpu nr)和 unbound cpu workqueue 的线程池就会通过 create_worker 创建一个 initial worker。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值