Work queue提供一個interface,讓使用者輕易的建立kernel thread並且將work綁在這個kernel thread上面,如下圖[1]所示。
由於work queue是建立一個kernel thread來執行,所以是在process context,不同於tasklet的interrupt context,因此,work queue可以sleep(設定semaphore或者執行block I/O等等)。
Creating Work
透過 DECLARE_WORK(name, void (work_func_t)(struct work_struct *work)); // statically
或者
INIT_WORK(struct work_struct*, void (work_func_t)(struct work_struct *work)); //dynamically
建立work,就是要執行的工作。
有了work還需要將它和work thread結合,您可以透過create_singlethread_workqueue("name")建立一個名為name的single thread(執行work的thread就稱為work thread),或者create_workqueue("name")建立per cpu的thread。接著就是要將work和work thread做關聯了,透過queue_work(work_thread, work)就可以將work送給work thread執行了。
queue_delayed_work(work_thread, delayed_work, delay)為queue_work()的delay版本。
flush_workqueue(work_thread)會wait直到這個work_thread的work都做完。flush_workqueue()並不會取消任何被delay執行的work,如果要取消delayed的work則需要呼叫cancel_delayed_work(delayed_work)將delayed_work自某個work thread中移除。
最後,要將work_thread摧毀要呼叫destroy_workqueue(work_thread)。
event/n
除了自己建立work thread以外,kernel還建立一個公用的work thread稱為event
kernel/workqueue.c
void __init init_workqueues(void) { … keventd_wq = create_workqueue("events"); … }
您可以透過schedule_work(&work)將,work送給"events"執行,flush_scheduled_work(void)等待"events"中所有的work執行完畢。
#include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> #include <linux/workqueue.h> #include <linux/sched.h> #include <linux/slab.h> MODULE_LICENSE("GPL"); static void brook_1_routine(struct work_struct *); static void brook_2_routine(struct work_struct *); static void brook_3_routine(struct work_struct *); static struct work_struct *brook_1_work; // for event static DECLARE_WORK(brook_2_work, brook_2_routine); static DECLARE_DELAYED_WORK(brook_3_work, brook_3_routine); static struct workqueue_struct *brook_workqueue; static int stop_wq; module_param(stop_wq, int, S_IRUGO | S_IWUGO); static int __init init_modules(void) { // for event brook_1_work = kzalloc(sizeof(typeof(*brook_1_work)), GFP_KERNEL); INIT_WORK(brook_1_work, brook_1_routine); schedule_work(brook_1_work); // for brook_wq brook_workqueue = create_workqueue("brook_wq"); queue_work(brook_workqueue, &brook_2_work); queue_delayed_work(brook_workqueue, &brook_3_work, 0); stop_wq = 0; return 0; } static void __exit exit_modules(void) { cancel_delayed_work(&brook_3_work); flush_workqueue(brook_workqueue); stop_wq = 1; destroy_workqueue(brook_workqueue); } static void brook_1_routine(struct work_struct *ws) { printk("%s(): on cpu:%d, pname:%s\n", __func__, smp_processor_id(), current->comm); } static void brook_2_routine(struct work_struct *ws) { printk("%s(): on cpu:%d, pname:%s\n", __func__, smp_processor_id(), current->comm); // do something to block/sleep // the work in the same workqueue is also deferred. msleep(5000); if (!stop_wq) { queue_work(brook_workqueue, &brook_2_work); } } static void brook_3_routine(struct work_struct *ws) { printk("%s(): on cpu:%d, pname:%s\n", __func__, smp_processor_id(), current->comm); queue_delayed_work(brook_workqueue, &brook_3_work, 50); } module_init(init_modules); module_exit(exit_modules);
Kernel Version:2.6.35
參考資料:
- http://www.embexperts.com/viewthread.php?tid=12&highlight=work%2Bqueue
- Linux Kernel Development 2nd, Novell Press