Linux設(shè)備驅(qū)動(dòng)workqueue(工作隊(duì)列)案例實(shí)現(xiàn)
掃描二維碼
隨時(shí)隨地手機(jī)看文章
工作隊(duì)列(work queue)是另外一種將工作推后執(zhí)行的形式,tasklet(小任務(wù)機(jī)制)有所不同。工作隊(duì)列可以把工作推后,交由一個(gè)內(nèi)核線程去執(zhí)行,也就是說(shuō),這個(gè)下半部分可以在進(jìn)程上下文中執(zhí)行。這樣,通過(guò)工作隊(duì)列執(zhí)行的代碼能占盡進(jìn)程上下文的所有優(yōu)勢(shì)。最重要的就是工作隊(duì)列允許被重新調(diào)度甚至是睡眠。
那么,什么情況下使用工作隊(duì)列,什么情況下使用tasklet呢?如果推后執(zhí)行的任務(wù)需要睡眠,那么就選擇工作隊(duì)列;如果推后執(zhí)行的任務(wù)不需要睡眠,那么就選擇tasklet。另外,如果需要用一個(gè)可以重新調(diào)度的實(shí)體來(lái)執(zhí)行你的下半部處理,也應(yīng)該使用工作隊(duì)列。它是唯一能在進(jìn)程上下文運(yùn)行的下半部實(shí)現(xiàn)的機(jī)制,也只有它才可以睡眠。這意味著在需要獲得大量的內(nèi)存時(shí)、在需要獲取信號(hào)量時(shí),在需要執(zhí)行阻塞式的I/O操作時(shí),它都會(huì)非常有用。如果不需要用一個(gè)內(nèi)核線程來(lái)推后執(zhí)行工作,那么就考慮使用tasklet。
一般,不要輕易的去使用工作隊(duì)列,因?yàn)槊慨?dāng)創(chuàng)建一條工作隊(duì)列,內(nèi)核就會(huì)為這條工作隊(duì)列創(chuàng)建一條內(nèi)核線程。工作隊(duì)列位于進(jìn)程上下文,與軟中斷,tasklet有所區(qū)別,工作隊(duì)列里允許延時(shí),睡眠操作,而軟中斷,tasklet位于中斷上下文,不允許睡眠和延時(shí)操作。
1、需要包含的頭文件
1#include <linux/workqueue.h>
2、工作隊(duì)列相關(guān)的數(shù)據(jù)結(jié)構(gòu)(各個(gè)版本內(nèi)核可能不同,這里用的是3.5)
1//工作隊(duì)列結(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、操作工作隊(duì)列相關(guān)的API
1創(chuàng)建一個(gè)隊(duì)列就會(huì)有一個(gè)內(nèi)核線程,一般不要輕易創(chuàng)建隊(duì)列
2位于進(jìn)程上下文--->可以睡眠
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)添加在隊(duì)列上
17
18延遲調(diào)度:
19 int schedule_delayed_work(struct work_struct *work, unsigned long delay);
20
21===========================================================
22
23創(chuàng)建新隊(duì)列和新工作者線程:
24 struct workqueue_struct *create_workqueue(const char *name);
25
26調(diào)度指定隊(duì)列:
27 int queue_work(struct workqueue_struct *wq, struct work_struct *work);
28
29延遲調(diào)度指定隊(duì)列:
30 int queue_delayed_work(struct workqueue_struct *wq,
31 struct work_struct *work, unsigned long delay);
32銷(xiāo)毀隊(duì)列:
33 void destroy_workqueue(struct workqueue_struct *wq);
4、Demo實(shí)現(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//定義一個(gè)工作隊(duì)列結(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//工作隊(duì)列處理函數(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);
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)建新隊(duì)列和新工作者線程
58 mywork = create_workqueue("my work");
59 //初始化
60 INIT_WORK(&work,mywork_fuc);
61 //調(diào)度指定隊(duì)列
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 //銷(xiāo)毀一條工作隊(duì)列
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下到板子上,重新啟動(dòng)會(huì)看到內(nèi)核打印信息
可以看到,當(dāng)我們按下按鍵的時(shí)候,進(jìn)入外部中斷服務(wù)函數(shù),此時(shí)task_fuc先被調(diào)用,然后調(diào)用到mywork_fuc,并打印了mywork_fuc里面的信息,從這里我們用程序驗(yàn)證了,工作隊(duì)列是位于進(jìn)程上下文,而不是中斷上下文,和tasklet是有所區(qū)別的,下一節(jié)我們將會(huì)講一講tasklet(小任務(wù)機(jī)制)。
一、廣志創(chuàng)新科技相關(guān)產(chǎn)品
二、業(yè)務(wù)聯(lián)系
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問(wèn)題,請(qǐng)聯(lián)系我們,謝謝!