Linux設(shè)備驅(qū)動workqueue(工作隊列)案例實現(xiàn)
掃描二維碼
隨時隨地手機看文章
工作隊列(work queue)是另外一種將工作推后執(zhí)行的形式,tasklet(小任務(wù)機制)有所不同。工作隊列可以把工作推后,交由一個內(nèi)核線程去執(zhí)行,也就是說,這個下半部分可以在進程上下文中執(zhí)行。這樣,通過工作隊列執(zhí)行的代碼能占盡進程上下文的所有優(yōu)勢。最重要的就是工作隊列允許被重新調(diào)度甚至是睡眠。
那么,什么情況下使用工作隊列,什么情況下使用tasklet呢?如果推后執(zhí)行的任務(wù)需要睡眠,那么就選擇工作隊列;如果推后執(zhí)行的任務(wù)不需要睡眠,那么就選擇tasklet。另外,如果需要用一個可以重新調(diào)度的實體來執(zhí)行你的下半部處理,也應(yīng)該使用工作隊列。它是唯一能在進程上下文運行的下半部實現(xiàn)的機制,也只有它才可以睡眠。這意味著在需要獲得大量的內(nèi)存時、在需要獲取信號量時,在需要執(zhí)行阻塞式的I/O操作時,它都會非常有用。如果不需要用一個內(nèi)核線程來推后執(zhí)行工作,那么就考慮使用tasklet。
一般,不要輕易的去使用工作隊列,因為每當(dāng)創(chuàng)建一條工作隊列,內(nèi)核就會為這條工作隊列創(chuàng)建一條內(nèi)核線程。工作隊列位于進程上下文,與軟中斷,tasklet有所區(qū)別,工作隊列里允許延時,睡眠操作,而軟中斷,tasklet位于中斷上下文,不允許睡眠和延時操作。
1、需要包含的頭文件
1#include <linux/workqueue.h>
2、工作隊列相關(guān)的數(shù)據(jù)結(jié)構(gòu)(各個版本內(nèi)核可能不同,這里用的是3.5)
1//工作隊列結(jié)構(gòu)
2struct work_struct {
3 atomic_long_t data;
4 //鏈表處理
5 struct list_head entry;
6 //工作處理函數(shù)
7 work_func_t func;
8#ifdef CONFIG_LOCKDEP
9 struct lockdep_map lockdep_map;
10#endif
11};
3、操作工作隊列相關(guān)的API
1創(chuàng)建一個隊列就會有一個內(nèi)核線程,一般不要輕易創(chuàng)建隊列
2位于進程上下文--->可以睡眠
3定義:
4 struct work_struct work;
5
6初始化:
7 INIT_WORK(struct work_struct *work, void (*func)(struct work_struct *work));
8
9定義并初始化:
10 DECLARE_WORK(name, void (*func)(struct work_struct *work));
11
12===========================================================
13
14調(diào)度:
15 int schedule_work(struct work_struct *work);
16 返回1成功, 0已經(jīng)添加在隊列上
17
18延遲調(diào)度:
19 int schedule_delayed_work(struct work_struct *work, unsigned long delay);
20
21===========================================================
22
23創(chuàng)建新隊列和新工作者線程:
24 struct workqueue_struct *create_workqueue(const char *name);
25
26調(diào)度指定隊列:
27 int queue_work(struct workqueue_struct *wq, struct work_struct *work);
28
29延遲調(diào)度指定隊列:
30 int queue_delayed_work(struct workqueue_struct *wq,
31 struct work_struct *work, unsigned long delay);
32銷毀隊列:
33 void destroy_workqueue(struct workqueue_struct *wq);
4、Demo實現(xiàn)(基于Tiny4412 Linux3.5內(nèi)核)
1#include <linux/module.h>
2#include <linux/kernel.h>
3#include <linux/init.h>
4#include <linux/platform_device.h>
5#include <linux/fb.h>
6#include <linux/backlight.h>
7#include <linux/err.h>
8#include <linux/pwm.h>
9#include <linux/slab.h>
10#include <linux/miscdevice.h>
11#include <linux/delay.h>
12#include <linux/gpio.h>
13#include <mach/gpio.h>
14#include <plat/gpio-cfg.h>
15#include <linux/timer.h> /*timer*/
16#include <asm/uaccess.h> /*jiffies*/
17#include <linux/delay.h>
18#include <linux/interrupt.h>
19#include <linux/workqueue.h>
20struct tasklet_struct task_t ;
21struct workqueue_struct *mywork ;
22//定義一個工作隊列結(jié)構(gòu)體
23struct work_struct work;
24static void task_fuc(unsigned long data)
25{
26 if(in_interrupt()){
27 printk("%s in interrupt handle!\n",__FUNCTION__);
28 }
29}
30//工作隊列處理函數(shù)
31static void mywork_fuc(struct work_struct *work)
32{
33 if(in_interrupt()){
34 printk("%s in interrupt handle!\n",__FUNCTION__);
35 }
36 msleep(2);
37 printk("%s in process handle!\n",__FUNCTION__);
38}
39
40static irqreturn_t irq_fuction(int irq, void *dev_id)
41{
42 tasklet_schedule(&task_t);
43 //調(diào)度工作
44 schedule_work(&work);
45 if(in_interrupt()){
46 printk("%s in interrupt handle!\n",__FUNCTION__);
47 }
48 printk("key_irq:%d\n",irq);
49 return IRQ_HANDLED ;
50}
51
52static int __init tiny4412_Key_irq_test_init(void)
53{
54 int err = 0 ;
55 int irq_num1 ;
56 int data_t = 100 ;
57 //創(chuàng)建新隊列和新工作者線程
58 mywork = create_workqueue("my work");
59 //初始化
60 INIT_WORK(&work,mywork_fuc);
61 //調(diào)度指定隊列
62 queue_work(mywork,&work);
63 tasklet_init(&task_t,task_fuc,data_t);
64 printk("irq_key init\n");
65 irq_num1 = gpio_to_irq(EXYNOS4_GPX3(2));
66 err = request_irq(irq_num1,irq_fuction,IRQF_TRIGGER_FALLING,"tiny4412_key1",(void *)"key1");
67 if(err != 0){
68 free_irq(irq_num1,(void *)"key1");
69 return -1 ;
70 }
71 return 0 ;
72}
73
74static void __exit tiny4412_Key_irq_test_exit(void)
75{
76 int irq_num1 ;
77 printk("irq_key exit\n");
78 irq_num1 = gpio_to_irq(EXYNOS4_GPX3(2));
79 //銷毀一條工作隊列
80 destroy_workqueue(mywork);
81 free_irq(irq_num1,(void *)"key1");
82}
83
84module_init(tiny4412_Key_irq_test_init);
85module_exit(tiny4412_Key_irq_test_exit);
86
87MODULE_LICENSE("GPL");
88MODULE_AUTHOR("YYX");
89MODULE_DESCRIPTION("Exynos4 KEY Driver");
將程序編譯完,將zImage下到板子上,重新啟動會看到內(nèi)核打印信息
可以看到,當(dāng)我們按下按鍵的時候,進入外部中斷服務(wù)函數(shù),此時task_fuc先被調(diào)用,然后調(diào)用到mywork_fuc,并打印了mywork_fuc里面的信息,從這里我們用程序驗證了,工作隊列是位于進程上下文,而不是中斷上下文,和tasklet是有所區(qū)別的,下一節(jié)我們將會講一講tasklet(小任務(wù)機制)。
一、廣志創(chuàng)新科技相關(guān)產(chǎn)品
二、業(yè)務(wù)聯(lián)系
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務(wù)。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!