VERSION:5.4.24
参考:
include/linux/workqueue.h
实际需要
定时器、下半部 tasklet,它们都是在中断上下文中执行,它们无法休眠。当要处理更复杂的事情时,往往更耗时。这些更耗时的工作放在定时器或是下半部中,会使得系统很卡;并且循环等待某件事情完成,这样太浪费 CPU 资源了。
如果使用线程来处理这些耗时的工作,那就可以解决系统卡顿的问题:因为线程可以休眠。
在内核中,我们并不需要自己去创建线程,可以使用“工作队列”(workqueue)。内核初始化工作队列时,就为它创建了内核线程。以后要使用“工作队列”,只需要把“工作”放入“工作队列中”,对应的内核线程就会取出“工作”,执行里面的函数。
重要的结构体,函数
-
核心 work_struct 结构体
/* * 只关注 func: 线程执行函数 */ struct work_struct { atomic_long_t data; struct list_head entry; work_func_t func; #ifdef CONFIG_LOCKDEP struct lockdep_map lockdep_map; #endif };
-
延时执行的 work
/* * 只关注 func: 线程执行函数 */ struct delayed_work { struct work_struct work; struct timer_list timer; /* target workqueue and CPU ->timer uses to queue ->work */ struct workqueue_struct *wq; int cpu; };
-
初始化工作队列
-
方法一
/* * @_work: work_struct 指针 * @_func: 要执行的函数 */ #define INIT_WORK(_work, _func) \ __INIT_WORK((_work), (_func), 0)
-
方法二
/* * @_work: delayed_work 指针 * @_func: 要执行的函数 */ #define INIT_DELAYED_WORK(_work, _func) \ __INIT_DELAYED_WORK(_work, _func, 0)
-
-
把工作任务放入全局工作队列
-
方法一
/** * schedule_work - 将工作任务放入全局工作队列 * @work:要完成的工作 * * 如果@work 已经在内核全局工作队列中,则返回 false,否则返回 true。 * * 如果作业尚未排队,则将其放入内核全局工作队列中,否则将其留在内核全局工作队列中的相同位置。 */ static inline bool schedule_work(struct work_struct *work) { return queue_work(system_wq, work); }
-
方法二
/** * schedule_delayed_work - 延迟后将工作任务放入全局工作队列 * @dwork:有待完成的工作 * @delay: 等待的 jiffies 数量或立即执行的 0 * * 等待给定时间后,这会将作业放入内核全局工作队列中。 */ static inline bool schedule_delayed_work(struct delayed_work *dwork, unsigned long delay) { return queue_delayed_work(system_wq, dwork, delay); }
-
内核工作队列使用示例一
-
第一步:构造结构体
/* 结构体 */ struct work_struct my_work; /* 任务函数 */ void work_func (struct work_struct *pwork) { /* user code */ }
-
第二步:初始化工作队列
/* * 在合适的位置添加这一句代码来初始化定时器 * 一般在驱动的 probe,open函数里使用 */ INIT_WORK(&my_work, work_func);
-
第三步:把工作任务放入全局工作队列
/* 在合适的位置添加这一句代码来启动任务 */ schedule_work(&my_work);
内核工作队列使用示例二
-
第一步:构造结构体
/* 结构体 */ struct delayed_work my_delayed_work; /* 任务函数 */ void work_func (struct work_struct *pwork) { /* user code */ }
-
第二步:初始化工作队列
/* * 在合适的位置添加这一句代码来初始化定时器 * 一般在驱动的 probe,open函数里使用 */ INIT_DELAYED_WORK(&my_delayed_work, work_func);
-
第三步:把工作任务放入全局工作队列
/* 在合适的位置添加这一句代码来启动任务 */ schedule_delayed_work(&my_delayed_work, msecs_to_jiffies(500)); /* 500ms 后执行 */