《Linux內(nèi)核設(shè)計與實(shí)現(xiàn)》讀書筆記(八)- 中斷下半部的處理
在前一章也提到過,之所以中斷會分成上下兩部分,是由于中斷對時限的要求非常高,需要盡快的響應(yīng)硬件。
主要內(nèi)容:
- 中斷下半部處理
- 實(shí)現(xiàn)中斷下半部的機(jī)制
- 總結(jié)中斷下半部的實(shí)現(xiàn)
- 中斷實(shí)現(xiàn)示例
1. 中斷下半部處理
那么對于一個中斷,如何劃分上下兩部分呢?哪些處理放在上半部,哪些處理放在下半部?
這里有一些經(jīng)驗可供借鑒:
- 如果一個任務(wù)對時間十分敏感,將其放在上半部
- 如果一個任務(wù)和硬件有關(guān),將其放在上半部
- 如果一個任務(wù)要保證不被其他中斷打斷,將其放在上半部
- 其他所有任務(wù),考慮放在下半部
2. 實(shí)現(xiàn)中斷下半部的機(jī)制
實(shí)現(xiàn)下半部的方法很多,隨著內(nèi)核的發(fā)展,產(chǎn)生了一些新的方法,也淘汰了一些舊方法。
目前使用最多的是以下3中方法
- 2.1 軟中斷
- 2.2 tasklet
- 2.3 工作隊列
2.1 軟中斷
軟中斷的代碼在:kernel/softirq.c
軟中斷的流程如下:
流程圖中幾個步驟的說明:
① 注冊軟中斷的函數(shù) open_softirq參見 kernel/softirq.c文件)
/* * 將軟中斷類型和軟中斷處理函數(shù)加入到軟中斷序列中 * @nr - 軟中斷類型 * @(*action)(struct softirq_action *) - 軟中斷處理的函數(shù)指針 */ void open_softirq(int nr, void (*action)(struct softirq_action *)) { /* softirq_vec是個struct softirq_action類型的數(shù)組 */ softirq_vec[nr].action = action; }
軟中斷類型目前有10個,其定義在 include/linux/interrupt.h 文件中:
enum { HI_SOFTIRQ=0, TIMER_SOFTIRQ, NET_TX_SOFTIRQ, NET_RX_SOFTIRQ, BLOCK_SOFTIRQ, BLOCK_IOPOLL_SOFTIRQ, TASKLET_SOFTIRQ, SCHED_SOFTIRQ, HRTIMER_SOFTIRQ, RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */ NR_SOFTIRQS };
struct softirq_action 的定義也在 include/linux/interrupt.h 文件中
/* * 這個結(jié)構(gòu)體的字段是個函數(shù)指針,字段名稱是action * 函數(shù)指針的返回指是void型 * 函數(shù)指針的參數(shù)是 struct softirq_action 的地址,其實(shí)就是指向 softirq_vec 中的某一項 * 如果 open_softirq 是這樣調(diào)用的: open_softirq(NET_TX_SOFTIRQ, my_tx_action); * 那么 my_tx_action 的參數(shù)就是 softirq_vec[NET_TX_SOFTIRQ]的地址 */ struct softirq_action { void (*action)(struct softirq_action *); };
② 觸發(fā)軟中斷的函數(shù) raise_softirq 參見 kernel/softirq.c文件
/* * 觸發(fā)某個中斷類型的軟中斷 * @nr - 被觸發(fā)的中斷類型 * 從函數(shù)中可以看出,在處理軟中斷前后有保存和恢復(fù)寄存器的操作 */ void raise_softirq(unsigned int nr) { unsigned long flags; local_irq_save(flags); raise_softirq_irqoff(nr); local_irq_restore(flags); }
③ 執(zhí)行軟中斷 do_softirq 參見 kernel/softirq.c文件
asmlinkage void do_softirq(void) { __u32 pending; unsigned long flags; /* 判斷是否在中斷處理中,如果正在中斷處理,就直接返回 */ if (in_interrupt()) return; /* 保存當(dāng)前寄存器的值 */ local_irq_save(flags); /* 取得當(dāng)前已注冊軟中斷的位圖 */ pending = local_softirq_pending(); /* 循環(huán)處理所有已注冊的軟中斷 */ if (pending) __do_softirq(); /* 恢復(fù)寄存器的值到中斷處理前 */ local_irq_restore(flags); }
④ 執(zhí)行相應(yīng)的軟中斷 - 執(zhí)行自己寫的中斷處理
linux中,執(zhí)行軟中斷有專門的內(nèi)核線程,每個處理器對應(yīng)一個線程,名稱ksoftirqd/n (n對應(yīng)處理器號)
通過top命令查看我的單核虛擬機(jī),CentOS系統(tǒng)中的ksoftirqd線程如下:
[root@vbox ~]# top | grep ksoftirq 4 root 20 0 0 0 0 S 0.0 0.0 0:00.02 ksoftirqd/0
2.2 tasklet
tasklet也是利用軟中斷來實(shí)現(xiàn)的,但是它提供了比軟中斷更好用的接口(其實(shí)就是基于軟中斷又封裝了一下),
所以除了對性能要求特別高的情況,一般建議使用tasklet來實(shí)現(xiàn)自己的中斷。
tasklet對應(yīng)的結(jié)構(gòu)體在 <linux/interrupt.h> 中
struct tasklet_struct { struct tasklet_struct *next; /* 鏈表中的下一個tasklet */ unsigned long state; /* tasklet狀態(tài) */ atomic_t count; /* 引用計數(shù)器 */ void (*func)(unsigned long); /* tasklet處理函數(shù) */ unsigned long data; /* tasklet處理函數(shù)的參數(shù) */ };
tasklet狀態(tài)只有3種值:
- 值 0 表示該tasklet沒有被調(diào)度
- 值 TASKLET_STATE_SCHED 表示該tasklet已經(jīng)被調(diào)度
- 值 TASKLET_STATE_RUN 表示該tasklet已經(jīng)運(yùn)行
引用計數(shù)器count 的值不為0,表示該tasklet被禁止。
tasklet使用流程如下:
1. 聲明tasklet (參見<linux/interrupt.h>)
/* 靜態(tài)聲明一個tasklet */ #define DECLARE_TASKLET(name, func, data) \ struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data } #define DECLARE_TASKLET_DISABLED(name, func, data) \ struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data } /* 動態(tài)聲明一個tasklet 傳遞一個tasklet_struct指針給初始化函數(shù) */ extern void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data);
2. 編寫處理程序
參照tasklet處理函數(shù)的原型來寫自己的處理邏輯
void tasklet_handler(unsigned long date)
3. 調(diào)度tasklet
中斷的上半部處理完后調(diào)度tasklet,在適當(dāng)時候進(jìn)行下半部的處理
tasklet_schedule(&my_tasklet) /* my_tasklet就是之前聲明的tasklet_struct */
2.3 工作隊列
工作隊列子系統(tǒng)是一個用于創(chuàng)建內(nèi)核線程的接口,通過它可以創(chuàng)建一個工作者線程來專門處理中斷的下半部工作。
工作隊列和tasklet不一樣,不是基于軟中斷來實(shí)現(xiàn)的。
缺省的工作者線程名稱是 events/n (n對應(yīng)處理器號)。
通過top命令查看我的單核虛擬機(jī),CentOS系統(tǒng)中的events線程如下:
[root@vbox ~]# top | grep event 7 root 20 0 0 0 0 S 0.0 0.0 0:03.71 events/0
工作隊列主要用到下面3個結(jié)構(gòu)體,弄懂了這3個結(jié)構(gòu)體的關(guān)系,也就知道工作隊列的處理流程了。
/* 在 include/linux/workqueue.h 文件中定義 */ struct work_struct { atomic_long_t data; /* 這個并不是處理函數(shù)的參數(shù),而是表示此work是否pending等狀態(tài)的flag */ #define WORK_STRUCT_PENDING 0 /* T if work item pending execution */ #define WORK_STRUCT_FLAG_MASK (3UL) #define WORK_STRUCT_WQ_DATA_MASK (~WORK_STRUCT_FLAG_MASK) struct list_head entry; /* 中斷下半部處理函數(shù)的鏈表 */ work_func_t func; /* 處理中斷下半部工作的函數(shù) */ #ifdef CONFIG_LOCKDEP struct lockdep_map lockdep_map; #endif }; /* 在 kernel/workqueue.c文件中定義 * 每個工作者線程對應(yīng)一個 cpu_workqueue_struct ,其中包含要處理的工作的鏈表 * (即 work_struct 的鏈表,當(dāng)此鏈表不空時,喚醒工作者線程來進(jìn)行處理) */ /* * The per-CPU workqueue (if single thread, we always use the first * possible cpu). */ struct cpu_workqueue_struct { spinlock_t lock; /* 鎖保護(hù)這種結(jié)構(gòu) */ struct list_head worklist; /* 工作隊列頭節(jié)點(diǎn) */ wait_queue_head_t more_work; struct work_struct *current_work; struct workqueue_struct *wq; /* 關(guān)聯(lián)工作隊列結(jié)構(gòu) */ struct task_struct *thread; /* 關(guān)聯(lián)線程 */ } ____cacheline_aligned; /* 也是在 kernel/workqueue.c 文件中定義的 * 每個 workqueue_struct 表示一種工作者類型,系統(tǒng)默認(rèn)的就是 events 工作者類型 * 每個工作者類型一般對應(yīng)n個工作者線程,n就是處理器的個數(shù) */ /* * The externally visible workqueue abstraction is an array of * per-CPU workqueues: */ struct workqueue_struct { struct cpu_workqueue_struct *cpu_wq; /* 工作者線程 */ struct list_head list; const char *name; int singlethread; int freezeable; /* Freeze threads during suspend */ int rt; #ifdef CONFIG_LOCKDEP struct lockdep_map lockdep_map; #endif };
使用工作者隊列的方法見下圖:
① 創(chuàng)建推后執(zhí)行的工作 - 有靜態(tài)創(chuàng)建和動態(tài)創(chuàng)建2種方法
/* 靜態(tài)創(chuàng)建一個work_struct * @n - work_struct結(jié)構(gòu)體,不用事先定義 * @f - 下半部處理函數(shù) */ #define DECLARE_WORK(n, f) \ struct work_struct n = __WORK_INITIALIZER(n, f) /* 動態(tài)創(chuàng)建一個 work_struct * @_work - 已經(jīng)定義好的一個 work_struct * @_func - 下半部處理函數(shù) */ #ifdef CONFIG_LOCKDEP #define INIT_WORK(_work, _func) \ do { \ static struct lock_class_key __key; \ \ (_work)->data = (atomic_long_t) WORK_DATA_INIT(); \ lockdep_init_map(&(_work)->lockdep_map, #_work, &__key, 0);\ INIT_LIST_HEAD(&(_work)->entry); \ PREPARE_WORK((_work), (_func)); \ } while (0) #else #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
工作隊列處理函數(shù)的原型:
typedef void (*work_func_t)(struct work_struct *work);
② 刷新現(xiàn)有的工作,這個步驟不是必須的,可以直接從第①步直接進(jìn)入第③步
刷新現(xiàn)有工作的意思就是在追加新的工作之前,保證隊列中的已有工作已經(jīng)執(zhí)行完了。
/* 刷新系統(tǒng)默認(rèn)的隊列,即 events 隊列 */ void flush_scheduled_work(void); /* 刷新用戶自定義的隊列 * @wq - 用戶自定義的隊列 */ void flush_workqueue(struct workqueue_struct *wq);
③ 調(diào)度工作 - 調(diào)度新定義的工作,使之處于等待處理器執(zhí)行的狀態(tài)
/* 調(diào)度第一步中新定義的工作,在系統(tǒng)默認(rèn)的工作者線程中執(zhí)行此工作 * @work - 第一步中定義的工作 */ schedule_work(struct work_struct *work); /* 調(diào)度第一步中新定義的工作,在系統(tǒng)默認(rèn)的工作者線程中執(zhí)行此工作 * @work - 第一步中定義的工作 * @delay - 延遲的時鐘節(jié)拍 */ int schedule_delayed_work(struct delayed_work *work, unsigned long delay); /* 調(diào)度第一步中新定義的工作,在用戶自定義的工作者線程中執(zhí)行此工作 * @wq - 用戶自定義的工作隊列類型 * @work - 第一步中定義的工作 */ int queue_work(struct workqueue_struct *wq, struct work_struct *work); /* 調(diào)度第一步中新定義的工作,在用戶自定義的工作者線程中執(zhí)行此工作 * @wq - 用戶自定義的工作隊列類型 * @work - 第一步中定義的工作 * @delay - 延遲的時鐘節(jié)拍 */ int queue_delayed_work(struct workqueue_struct *wq, struct delayed_work *work, unsigned long delay);
3. 總結(jié)中斷下半部的實(shí)現(xiàn)
下面對實(shí)現(xiàn)中斷下半部工作的3種機(jī)制進(jìn)行總結(jié),便于在實(shí)際使用中決定使用哪種機(jī)制
|
下半部機(jī)制 |
上下文 |
復(fù)雜度 |
執(zhí)行性能 |
順序執(zhí)行保障 |
| 軟中斷 | 中斷 | 高 (需要自己確保軟中斷的執(zhí)行順序及鎖機(jī)制) |
好
(全部自己實(shí)現(xiàn),便于調(diào)優(yōu)) |
沒有 |
| tasklet | 中斷 | 中
(提供了簡單的接口來使用軟中斷) |
中 | 同類型不能同時執(zhí)行 |
| 工作隊列 | 進(jìn)程 | 低
(在進(jìn)程上下文中運(yùn)行,與寫用戶程序差不多) |
差 | 沒有
(和進(jìn)程上下文一樣被調(diào)度) |
4. 中斷實(shí)現(xiàn)示例
4.1 軟中斷的實(shí)現(xiàn)
本來想用內(nèi)核模塊的方法來測試一下軟中斷的流程,但是編譯時發(fā)現(xiàn)軟中斷注冊函數(shù)(open_softirq)和觸發(fā)函數(shù)(raise_softirq)
并沒有用EXPORT_SYMBOL導(dǎo)出,所以自定義的內(nèi)核模塊中無法使用。
測試的代碼如下:
#include <linux/interrupt.h> #include "kn_common.h" MODULE_LICENSE("Dual BSD/GPL"); static void my_softirq_func(struct softirq_action*); static int testsoftirq_init(void) { // 注冊softirq,這里注冊的是定時器的下半部 open_softirq(TIMER_SOFTIRQ, my_softirq_func); // 觸發(fā)softirq raise_softirq(TIMER_SOFTIRQ); return 0; } static void testsoftirq_exit(void) { printk(KERN_ALERT "*************************\n"); print_current_time(0); printk(KERN_ALERT "testrbtree is exited!\n"); printk(KERN_ALERT "*************************\n"); } static void my_softirq_func(struct softirq_action* act) { printk(KERN_ALERT "=========================\n"); print_current_time(0); printk(KERN_ALERT "my softirq function is been called!....\n"); printk(KERN_ALERT "=========================\n"); } module_init(testsoftirq_init); module_exit(testsoftirq_exit);
其中頭文件 kn_common.h 的相關(guān)內(nèi)容參見之前的博客《Linux內(nèi)核設(shè)計與實(shí)現(xiàn)》讀書筆記(六)- 內(nèi)核數(shù)據(jù)結(jié)構(gòu)
由于內(nèi)核沒有用EXPORT_SYMBOL導(dǎo)出open_softirq和raise_softirq函數(shù),所以編譯時有如下警告:
WARNING: "open_softirq" [/root/chap08/mysoftirq.ko] undefined! WARNING: "raise_softirq" [/root/chap08/mysoftirq.ko] undefined!
注:編譯用的系統(tǒng)時centos6.3 (uname -r結(jié)果 - 2.6.32-279.el6.x86_64)
沒辦法,只能嘗試修改內(nèi)核代碼(將open_softirq和raise_softirq用EXPORT_SYMBOL導(dǎo)出),再重新編譯內(nèi)核,然后再嘗試能否測試軟中斷。
主要修改2個文件,(既然要修改代碼,干脆加了一種軟中斷類型):
/* 修改 kernel/softirq.c */ // ... 略 ... char *softirq_to_name[NR_SOFTIRQS] = { "HI", "TIMER", "NET_TX", "NET_RX", "BLOCK", "BLOCK_IOPOLL", "TASKLET", "SCHED", "HRTIMER", "RCU", "WYB" }; /* 追加了一種新的softirq,即 "WYB",我名字的縮寫 ^_^ */ // ... 略 ... void raise_softirq(unsigned int nr) { unsigned long flags; local_irq_save(flags); raise_softirq_irqoff(nr); local_irq_restore(flags); } EXPORT_SYMBOL(raise_softirq); /* 追加的代碼 */ void open_softirq(int nr, void (*action)(struct softirq_action *)) { softirq_vec[nr].action = action; } EXPORT_SYMBOL(open_softirq); /* 追加的代碼 */ // ... 略 ... /* 還修改了 include/linux/interrupt.h */ enum { HI_SOFTIRQ=0, TIMER_SOFTIRQ, NET_TX_SOFTIRQ, NET_RX_SOFTIRQ, BLOCK_SOFTIRQ, BLOCK_IOPOLL_SOFTIRQ, TASKLET_SOFTIRQ, SCHED_SOFTIRQ, HRTIMER_SOFTIRQ, RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */ WYB_SOFTIRQS, /* 追加的一種中斷類型 */ NR_SOFTIRQS };
重新編譯內(nèi)核后,在新的內(nèi)核上再次實(shí)驗軟中斷代碼:
(編譯內(nèi)核方法參見:《Linux內(nèi)核設(shè)計與實(shí)現(xiàn)》讀書筆記(五)- 系統(tǒng)調(diào)用 3.3節(jié))
測試軟中斷的代碼:testsoftirq.c
#include <linux/interrupt.h> #include "kn_common.h" MODULE_LICENSE("Dual BSD/GPL"); static void my_softirq_func(struct softirq_action*); static int testsoftirq_init(void) { printk(KERN_ALERT "interrupt's top half!\n"); // 注冊softirq,這里注冊的是自定義的軟中斷類型 open_softirq(WYB_SOFTIRQS, my_softirq_func); // 觸發(fā)softirq raise_softirq(WYB_SOFTIRQS); return 0; } static void testsoftirq_exit(void) { printk(KERN_ALERT "*************************\n"); print_current_time(0); printk(KERN_ALERT "testsoftirq is exited!\n"); printk(KERN_ALERT "*************************\n"); } static void my_softirq_func(struct softirq_action* act) { printk(KERN_ALERT "=========================\n"); print_current_time(0); printk(KERN_ALERT "my softirq function is been called!....\n"); printk(KERN_ALERT "=========================\n"); } module_init(testsoftirq_init); module_exit(testsoftirq_exit);
Makefile:
obj-m += mysoftirq.o mysoftirq-objs := testsoftirq.o kn_common.o #generate the path CURRENT_PATH:=$(shell pwd) #the current kernel version number LINUX_KERNEL:=$(shell uname -r) #the absolute path LINUX_KERNEL_PATH:=/usr/src/kernels/$(LINUX_KERNEL) #complie object all: make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules rm -rf modules.order Module.symvers .*.cmd *.o *.mod.c .tmp_versions *.unsigned #clean clean: rm -rf modules.order Module.symvers .*.cmd *.o *.mod.c *.ko .tmp_versions *.unsigned
測試軟中斷的方法如下:
make insmod mysoftirq.ko rmmod mysoftirq dmesg | tail -9 # 運(yùn)行結(jié)果 interrupt's top half! ========================= 2013-4-22 14:4:57 my softirq function is been called!.... ========================= ************************* 2013-4-22 14:5:2 testsoftirq is exited! *************************
4.2 tasklet的實(shí)現(xiàn)
tasklet的實(shí)驗用默認(rèn)的內(nèi)核即可,我們切換到centos6.3的默認(rèn)內(nèi)核(uname -r: 2.6.32-279.el6.x86_64)
從中我們也可以看出,內(nèi)核之所以沒有導(dǎo)出open_softirq和raise_softirq函數(shù),可能還是因為提倡我們盡量用tasklet來實(shí)現(xiàn)中斷的下半部工作。
tasklet測試代碼:testtasklet.c
#include <linux/interrupt.h> #include "kn_common.h" MODULE_LICENSE("Dual BSD/GPL"); static void my_tasklet_func(unsigned long); /* mytasklet 必須定義在testtasklet_init函數(shù)的外面,否則會出錯 */ DECLARE_TASKLET(mytasklet, my_tasklet_func, 1000); static int testtasklet_init(void) { printk(KERN_ALERT "interrupt's top half!\n"); // 如果在這里定義的話,那么 mytasklet是函數(shù)的局部變量, // 后面調(diào)度的時候會找不到 mytasklet // DECLARE_TASKLET(mytasklet, my_tasklet_func, 1000); // 調(diào)度tasklet, 處理器會在適當(dāng)時候執(zhí)行這個tasklet tasklet_schedule(&mytasklet); return 0; } static void testtasklet_exit(void) { printk(KERN_ALERT "*************************\n"); print_current_time(0); printk(KERN_ALERT "testtasklet is exited!\n"); printk(KERN_ALERT "*************************\n"); } static void my_tasklet_func(unsigned long data) { printk(KERN_ALERT "=========================\n"); print_current_time(0); printk(KERN_ALERT "my tasklet function is been called!....\n"); printk(KERN_ALERT "parameter data is %ld\n", data); printk(KERN_ALERT "=========================\n"); } module_init(testtasklet_init); module_exit(testtasklet_exit);
Makefile:
obj-m += mytasklet.o mytasklet-objs := testtasklet.o kn_common.o #generate the path CURRENT_PATH:=$(shell pwd) #the current kernel version number LINUX_KERNEL:=$(shell uname -r) #the absolute path LINUX_KERNEL_PATH:=/usr/src/kernels/$(LINUX_KERNEL) #complie object all: make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules rm -rf modules.order Module.symvers .*.cmd *.o *.mod.c .tmp_versions *.unsigned #clean clean: rm -rf modules.order Module.symvers .*.cmd *.o *.mod.c *.ko .tmp_versions *.unsigned
測試tasklet的方法如下:
make insmod mytasklet.ko rmmod mytasklet dmesg | tail -10 # 運(yùn)行結(jié)果 interrupt's top half! ========================= 2013-4-22 14:53:14 my tasklet function is been called!.... parameter data is 1000 ========================= ************************* 2013-4-22 14:53:20 testtasklet is exited! *************************
4.3 工作隊列的實(shí)現(xiàn)
workqueue的例子的中靜態(tài)定義了一個工作,動態(tài)定義了一個工作。
靜態(tài)定義的工作由系統(tǒng)工作隊列(events/n)調(diào)度,
動態(tài)定義的工作由自定義的工作隊列(myworkqueue)調(diào)度。
測試工作隊列的代碼:testworkqueue.c
#include <linux/workqueue.h> #include "kn_common.h" MODULE_LICENSE("Dual BSD/GPL"); static void my_work_func(struct work_struct *); static void my_custom_workqueue_func(struct work_struct *); /* 靜態(tài)創(chuàng)建一個工作,使用系統(tǒng)默認(rèn)的工作者線程,即 events/n */ DECLARE_WORK(mywork, my_work_func); static int testworkqueue_init(void) { /*自定義的workqueue */ struct workqueue_struct *myworkqueue = create_workqueue("myworkqueue"); /* 動態(tài)創(chuàng)建一個工作 */ struct work_struct *mywork2; mywork2 = kmalloc(sizeof(struct work_struct), GFP_KERNEL); INIT_WORK(mywork2, my_custom_workqueue_func); printk(KERN_ALERT "interrupt's top half!\n"); /* 刷新系統(tǒng)默認(rèn)的隊列 */ flush_scheduled_work(); /* 調(diào)度工作 */ schedule_work(&mywork); /* 刷新自定義的工作隊列 */ flush_workqueue(myworkqueue); /* 調(diào)度自定義工作隊列上的工作 */ queue_work(myworkqueue, mywork2); return 0; } static void testworkqueue_exit(void) { printk(KERN_ALERT "*************************\n"); print_current_time(0); printk(KERN_ALERT "my workqueue test is exited!\n"); printk(KERN_ALERT "*************************\n"); } static void my_work_func(struct work_struct *work) { printk(KERN_ALERT "=========================\n"); print_current_time(0); printk(KERN_ALERT "my workqueue function is been called!....\n"); printk(KERN_ALERT "=========================\n"); } static void my_custom_workqueue_func(struct work_struct *work) { printk(KERN_ALERT "=========================\n"); print_current_time(0); printk(KERN_ALERT "my cutomize workqueue function is been called!....\n"); printk(KERN_ALERT "=========================\n"); kfree(work); } module_init(testworkqueue_init); module_exit(testworkqueue_exit);
Makefile:
obj-m += myworkqueue.o myworkqueue-objs := testworkqueue.o kn_common.o #generate the path CURRENT_PATH:=$(shell pwd) #the current kernel version number LINUX_KERNEL:=$(shell uname -r) #the absolute path LINUX_KERNEL_PATH:=/usr/src/kernels/$(LINUX_KERNEL) #complie object all: make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules rm -rf modules.order Module.symvers .*.cmd *.o *.mod.c .tmp_versions *.unsigned #clean clean: rm -rf modules.order Module.symvers .*.cmd *.o *.mod.c *.ko .tmp_versions *.unsigned
測試workqueue的方法如下:
make insmod myworkqueue.ko rmmod myworkqueue dmesg | tail -13 # 運(yùn)行結(jié)果 interrupt's top half! ========================= 2013-4-23 9:55:29 my workqueue function is been called!.... ========================= ========================= 2013-4-23 9:55:29 my cutomize workqueue function is been called!.... ========================= ************************* 2013-4-23 9:55:29 my workqueue is exited! *************************



浙公網(wǎng)安備 33010602011771號