本文部分參考華清遠見文檔
中斷上半部要求執行時間間隔段,所以往往將處理時間較長的代碼放在中斷下半部來處理
中斷下半部的應用:網卡驅動上半部初始化網卡驅動等短時間的事件,下半部收發數據
中斷下半部:
a, 下半部產生的原因:
1,中斷上下文中不能阻塞,這也限制了中斷上下文中能幹的事
2,中斷處理函數執行過程中仍有可能被其他中斷打斷,都希望中斷處理函數執行得越快越好。
基於上面的原因,內核將整個的中斷處理流程分爲了上半部和下半部。上半部就是之前所說的中斷處理函數,它能最快的響應中斷,並且做一些必須在中斷響應之後馬上要做的事情。而一些需要在中斷處理函數後繼續執行的操作,內核建議把它放在下半部執行。
比如:在linux內核中,當網卡一旦接受到數據,網卡會通過中斷告訴內核處理數據,內核會在網卡中斷處理函數(上半部)執行一些網卡硬件的必要設置,因爲這是在中斷響應後急切要乾的事情。接着,內核調用對應的下半部函數來處理網卡接收到的數據,因爲數據處理沒必要在中斷處理函數裏面馬上執行,可以將中斷讓出來做更緊迫的事情
b,中斷下半部實現的機制分類
tasklet:
workqueue:工作隊列
timer:定時器
其實還有一種,叫softirq,但要寫代碼的話,就必須修改原來的內核框架代碼,在實際開發中用的比較少,tasklet內部實現就是用softeirq
c, 中斷下半部實現方法
1, tasklet的編程方式
1.1 : 定義並初始化結構體tasklet_struct(一般在哪裏初始化:是在模塊卸載方法中)
struct tasklet_struct
{
struct tasklet_struct *next; // l鏈表
unsigned long state;
atomic_t count;
void (*func)(unsigned long); // 下半部處理方法的實現
unsigned long data;//給處理函數的傳參
};
初始化方式:
靜態:DECLARE_TASKLET(name, func, data);
DCLARE_TASKLET_DISABLED初始化後的處於禁止狀態,暫時不能被使用(不是中斷),除非被激活
參數1:tasklet_struct 的變量名字,自定義
參數2:中斷下半部執行的處理函數.類型爲函數指針
參數3:處理函數帶的參數
動態:void tasklet_init(struct tasklet_struct * t, void(* func)(unsigned long), unsigned long data);
參數1:tasklet_struct 對象
參數2:中斷下半部執行的處理函數
參數3:處理函數帶的參數
1.2: 在中斷上半部中調度下半部
void tasklet_schedule(struct tasklet_struct * t);
1.3: 在模塊卸載時,銷燬這個tasklet_struct 對象
void tasklet_kill(struct tasklet_struct *t)
1.4:原理:初始化好struct tasklet_struct對象後,tasklet_schedule()會將tasklet對象加到鏈表中,內核稍後會去調度這個tasklet對象
1.5: 特點:優先級高,調度快,運行在中斷上下文中,所以在處理方法中,不能執行阻塞/睡眠的操作
2,workqueque編程方式:
2.1 :定義並初始化workqueue_struct(一個隊列)和work_struct(隊列中的一項工作)對象
work_struct對象的初始化
struct work_struct {
atomic_long_t data; // 傳遞給work的參數
struct list_head entry; // 所在隊列的鏈表
work_func_t func; // work對應的處理方法
};
靜態:DECLARE_WORK(n, f)
參數1: 變量名,自定義
參數2:work對應的處理方法,類型爲函數指針
動態:INIT_WORK(_work, _func)
參數1: 指針,先聲明一個struct work_struct變量,將變量地址填入
參數2:work對應的處理方法,類型爲函數指針
返回值: 返回值爲void
workqueue_struct對象的初始化:(其實就是一個內核線程)
1, 重新創建一個隊列
create_workqueue(name)//這個本身就是一個宏
參數:名字,自定義,用於識別
返回值:struct workqueue_struct *
2, 系統在開機的時候自動創建一個隊列
2.2 將工作對象加入工作隊列中,並參與調度(注意不是馬上調度,該步驟也是中斷上半部中調用)
int queue_work(struct workqueue_struct *wq, struct work_struct *work)
參數1:工作隊列
參數2: 工作對象
返回值: 0表示已經放到隊列上了(也即時說本次屬於重複操作),其實一般我們都不會去做出錯處理
2.3 在模塊註銷的時候,銷燬工作隊列和工作對象
void flush_workqueue(struct workqueue_struct * wq)
該函數會一直等待,知道指定的等待隊列中所有的任務都執行完畢並從等待隊列中移除。
void destroy_workqueue(struct workqueue_struct * wq);
該函數是是創建等待隊列的反操作,註銷掉指定的等待隊列。
2.4: 對於使用內核自帶的工作隊列events, 操作步驟如下:
2.4.1 初始化工作對象,無需創建隊列了
靜態:DECLARE_WORK(n, f)
動態:INIT_WORK(_work, _func)
2.4.2將工作加入隊列並調度(在中斷上半部中調度)
int schedule_work(struct work_struct * work)
只要兩步驟就完成,也不需要刷新,也不要銷燬,因爲這個工作隊列是系統管理的,我們不用管
2.5:原理梳理:在工作隊列中,有專門的工作者線程來處理加入到工作對列中的任務。工作對列對應的工作者線程可能不止一個,每個處理器有且僅有一個工作隊列 對應的工作者線程,在內核中有一個默認的工作隊列events,對於單處理器只有一個對應的工作者線程
3, 定時器timer編程方式:(以上兩個下半部處理都是內核在一個特定的時候進行調度,時間不定,而timer可以指定某個時間點執行)
3.1, jiffies,表示從系統啓動到當前的時間值,一般做加法(+5HZ(5s),)
3.2, 定義並初始化 timer_list對象
struct timer_list {
struct list_head entry; // 鏈表
unsigned long expires; // 過期時間。也及時在什麼時候執行處理方法
struct tvec_base *base;
void (*function)(unsigned long); // 處理方法
unsigned long data; // 處理方法可以傳遞的參數
int slack;
};
靜態初始化:TIMER_INITIALIZER(_function, _expires, _data)
動態初始化:void init_timer(timer)
參數:爲一個指針,需要傳遞一個struct timer_list對象的地址
該函數只是初始化了timer_list對象的部分成員,還有以下成員是需要編程的:
struct timer_list mytimer;
init_timer(&mytimer);
mytimer.expires = jiffies + 2HZ
mytimer.fuction = my_timer_func; // 自己去實現
mytimer.data = (unsigned long)99; // 可以傳遞參數
3.3, 激活timer,開始計時 (一般也是放在中斷上半部完成)
void add_timer(&mytimer);
3.4 計時結束是,也就是要執行處理函數時,執行函數中要下一次計時的話,必須修改timer
mod_timer(&my_timer, jiffies + 2*HZ);
// 2s之後再來,相當於如下:
my_timer.expires = jiffies + 2*HZ; //重新設定時間,在兩秒後再執行
add_timer(&my_timer); //再次激活定時器
3.5 定時器的銷燬
int del_timer(struct timer_list *timer) // 該函數用來刪除還沒超時的定時器
timer定時器的中斷上下半部代碼
#include <linux/kernel.h> #include <linux/device.h> #include <linux/cdev.h> #include <linux/fs.h> #include <linux/err.h> #include <linux/irq.h> #include <linux/input.h> #include <linux/interrupt.h> #include <linux/timer.h> #include <asm-arm/param.h> #include <asm/arch/regs-gpio.h> #include <asm/gpio.h> #define VIRTUAL_MAJOR 250 int major = VIRTUAL_MAJOR; struct myirq_desc{ int irq_id; char *irq_name; int irq_code; }; struct myirq_desc myirq_descs[3]= { {S3C2410_GPF0, "s2", KEY_A}, {S3C2410_GPF2, "s3", KEY_K}, {S3C2410_GPG3, "s4", KEY_Z}, }; struct VirtualDisk{ struct class *mycdevclass;//在/sys/class創建類 struct class_device *mycdevclassdevice;//在/dev下創建設備 struct cdev mycdev;//給設備添加相關的fileoption struct timer_list mytimer; }; struct VirtualDisk *myvirtualdisk; static struct file_operations mydev_fops = { .owner = THIS_MODULE, }; static void mytimer_func(unsigned long fundata){ printk("*******%s********\n", __FUNCTION__); struct VirtualDisk *myvirtualdisk_fun = (struct VirtualDisk *)(fundata); myvirtualdisk_fun->mytimer.expires =jiffies + 2 * HZ; add_timer(&myvirtualdisk_fun->mytimer); printk("timer func happened!\n"); } static irqreturn_t myirq_handle(int irq, void *dev_id){ struct myirq_desc *myirq_descone = (struct myirq_desc *)dev_id; printk("*******%s********\n", __FUNCTION__); printk("irq = %d, irq_id = %d,irq_name = %s, irq_code = %c\n", irq, myirq_descone->irq_id, myirq_descone->irq_name, myirq_descone->irq_code); mod_timer(&myvirtualdisk->mytimer, jiffies + 2*HZ); return IRQ_RETVAL(IRQ_HANDLED); } static int __init cdevtest_init(void){ dev_t mydev = MKDEV(major, 0); int ret; int i = 0; printk("*******%s********\n", __FUNCTION__); if(major){//註冊proc/devices ret = register_chrdev_region(mydev, 1, "mynewdriver"); }else{ ret = alloc_chrdev_region(&mydev, 0, 1, "mynewdriver"); major = MAJOR(mydev); } if(ret < 0){ printk(KERN_ERR "register_chrdev_region failed!\n"); ret = -EINVAL; return ret; } myvirtualdisk = kmalloc(sizeof(struct VirtualDisk), GFP_KERNEL); if(!myvirtualdisk){ ret = -ENOMEM; printk(KERN_ERR "kmalloc myvirtualdisk failed!\n"); goto release_chrdev; } myvirtualdisk->mycdevclass = class_create(THIS_MODULE, "mynewdriver"); if(IS_ERR(myvirtualdisk->mycdevclass)){ ret = PTR_ERR(myvirtualdisk->mycdevclass); printk(KERN_ERR "class_create failed!\n"); goto release_mem_malloc; } myvirtualdisk->mycdevclassdevice = class_device_create(myvirtualdisk->mycdevclass, NULL, MKDEV(major, 0), NULL, "mynewdriver"); if(IS_ERR(myvirtualdisk->mycdevclassdevice)){ ret = PTR_ERR(myvirtualdisk->mycdevclassdevice); printk(KERN_ERR "class_device_create failed!\n"); goto release_class_create; } cdev_init(&(myvirtualdisk->mycdev), &mydev_fops); myvirtualdisk->mycdev.owner = THIS_MODULE; ret = cdev_add(&myvirtualdisk->mycdev, MKDEV(major, 0), 1); //這裏把timer相關的放在irq前面,不然會有bug出現(當在insmod時候按下按鈕就會出錯) init_timer(&myvirtualdisk->mytimer); myvirtualdisk->mytimer.function = mytimer_func; myvirtualdisk->mytimer.data = (unsigned long)myvirtualdisk; if(ret < 0){ goto release_device_class_create; } for(i = 0; i < 3; i++) { ret = request_irq(gpio_to_irq(myirq_descs[i].irq_id), myirq_handle, IRQF_TRIGGER_FALLING, myirq_descs[i].irq_name, &myirq_descs[i]); if(ret < 0){ printk(KERN_ERR "request irq failed!\n"); ret =-EFAULT; goto release_cdevandtimer; } } return 0; release_cdevandtimer: del_timer(&myvirtualdisk->mytimer); cdev_del(&myvirtualdisk->mycdev); release_device_class_create: class_device_unregister(myvirtualdisk->mycdevclassdevice); release_class_create: class_destroy(myvirtualdisk->mycdevclass); release_mem_malloc: kfree(myvirtualdisk); release_chrdev: unregister_chrdev_region(MKDEV(major, 0), 1); return ret; } static void __exit cdevtest_exit(void){ int i = 0; printk("*******%s****1****\n", __FUNCTION__); del_timer(&myvirtualdisk->mytimer); printk("*******%s*****2***\n", __FUNCTION__); for(i = 0; i < 3; i++)free_irq(gpio_to_irq(myirq_descs[i].irq_id), &myirq_descs[i]); printk("*******%s*****3***\n", __FUNCTION__); cdev_del(&myvirtualdisk->mycdev); printk("*******%s*****4***\n", __FUNCTION__); class_device_unregister(myvirtualdisk->mycdevclassdevice); printk("*******%s*****5***\n", __FUNCTION__); class_destroy(myvirtualdisk->mycdevclass); printk("*******%s*****6***\n", __FUNCTION__); kfree(myvirtualdisk); printk("*******%s********\n", __FUNCTION__); unregister_chrdev_region(MKDEV(major, 0), 1); printk("*******%s*****7***\n", __FUNCTION__); } module_init(cdevtest_init); module_exit(cdevtest_exit); MODULE_LICENSE("GPL");
完整的tasklet任務中斷上下半部代碼
#include <linux/kernel.h> #include <linux/device.h> #include <linux/cdev.h> #include <linux/fs.h> #include <linux/err.h> #include <linux/irq.h> #include <linux/input.h> #include <linux/interrupt.h> #include <linux/timer.h> #include <asm-arm/param.h> #include <asm/arch/regs-gpio.h> #include <asm/gpio.h> #define VIRTUAL_MAJOR 250 int major = VIRTUAL_MAJOR; struct myirq_desc{ int irq_id; char *irq_name; int irq_code; }; struct myirq_desc myirq_descs[3]= { {S3C2410_GPF0, "s2", KEY_J}, {S3C2410_GPF2, "s3", KEY_K}, {S3C2410_GPG3, "s4", KEY_Z}, }; struct VirtualDisk{ struct class *mycdevclass;//在/sys/class創建類 struct class_device *mycdevclassdevice;//在/dev下創建設備 struct cdev mycdev;//給設備添加相關的fileoption struct timer_list mytimer; struct tasklet_struct mytasklet; }; struct VirtualDisk *myvirtualdisk; static struct file_operations mydev_fops = { .owner = THIS_MODULE, }; static void mytasklet_func(unsigned long fundata){ printk("*****%s******,date = %ld\n", __FUNCTION__, fundata); } static void mytimer_func(unsigned long fundata){ printk("*******%s********\n", __FUNCTION__); struct VirtualDisk *myvirtualdisk_fun = (struct VirtualDisk *)(fundata); //myvirtualdisk_fun->mytimer.expires =jiffies + 2 * HZ; //add_timer(&myvirtualdisk_fun->mytimer); printk("timer func happened!\n"); } static irqreturn_t myirq_handle(int irq, void *dev_id){ struct myirq_desc *myirq_descone = (struct myirq_desc *)dev_id; printk("*******%s********\n", __FUNCTION__); printk("irq = %d, irq_id = %d,irq_name = %s, irq_code = %c\n", irq, myirq_descone->irq_id, myirq_descone->irq_name, myirq_descone->irq_code); tasklet_schedule(&myvirtualdisk->mytasklet);//激發任務,將mytasklet_func加入系統任務 mod_timer(&myvirtualdisk->mytimer, jiffies + 2*HZ); return IRQ_RETVAL(IRQ_HANDLED); } static int __init cdevtest_init(void){ dev_t mydev = MKDEV(major, 0); int ret; int i = 0; printk("*******%s********\n", __FUNCTION__); if(major){//註冊proc/devices ret = register_chrdev_region(mydev, 1, "mynewdriver"); }else{ ret = alloc_chrdev_region(&mydev, 0, 1, "mynewdriver"); major = MAJOR(mydev); } if(ret < 0){ printk(KERN_ERR "register_chrdev_region failed!\n"); ret = -EINVAL; return ret; } myvirtualdisk = kmalloc(sizeof(struct VirtualDisk), GFP_KERNEL); if(!myvirtualdisk){ ret = -ENOMEM; printk(KERN_ERR "kmalloc myvirtualdisk failed!\n"); goto release_chrdev; } myvirtualdisk->mycdevclass = class_create(THIS_MODULE, "mynewdriver"); if(IS_ERR(myvirtualdisk->mycdevclass)){ ret = PTR_ERR(myvirtualdisk->mycdevclass); printk(KERN_ERR "class_create failed!\n"); goto release_mem_malloc; } myvirtualdisk->mycdevclassdevice = class_device_create(myvirtualdisk->mycdevclass, NULL, MKDEV(major, 0), NULL, "mynewdriver"); if(IS_ERR(myvirtualdisk->mycdevclassdevice)){ ret = PTR_ERR(myvirtualdisk->mycdevclassdevice); printk(KERN_ERR "class_device_create failed!\n"); goto release_class_create; } cdev_init(&(myvirtualdisk->mycdev), &mydev_fops); myvirtualdisk->mycdev.owner = THIS_MODULE; ret = cdev_add(&myvirtualdisk->mycdev, MKDEV(major, 0), 1); //tasklet 任務調度 tasklet_init(&myvirtualdisk->mytasklet, mytasklet_func, (unsigned long) 90); //這裏把timer相關的放在irq前面,不然會有bug出現(當在insmod時候按下按鈕就會出錯) init_timer(&myvirtualdisk->mytimer); myvirtualdisk->mytimer.function = mytimer_func; myvirtualdisk->mytimer.data = (unsigned long)myvirtualdisk; if(ret < 0){ goto release_device_class_create; } for(i = 0; i < 3; i++) { ret = request_irq(gpio_to_irq(myirq_descs[i].irq_id), myirq_handle, IRQF_TRIGGER_FALLING, myirq_descs[i].irq_name, &myirq_descs[i]); if(ret < 0){ printk(KERN_ERR "request irq failed!\n"); ret =-EFAULT; goto release_cdevandtimer; } } return 0; release_cdevandtimer: tasklet_kill(&myvirtualdisk->mytasklet);//刪除任務 del_timer(&myvirtualdisk->mytimer); cdev_del(&myvirtualdisk->mycdev); release_device_class_create: class_device_unregister(myvirtualdisk->mycdevclassdevice); release_class_create: class_destroy(myvirtualdisk->mycdevclass); release_mem_malloc: kfree(myvirtualdisk); release_chrdev: unregister_chrdev_region(MKDEV(major, 0), 1); return ret; } static void __exit cdevtest_exit(void){ int i = 0; printk("*******%s****1****\n", __FUNCTION__); tasklet_kill(&myvirtualdisk->mytasklet);//刪除任務 del_timer(&myvirtualdisk->mytimer); printk("*******%s*****2***\n", __FUNCTION__); for(i = 0; i < 3; i++)free_irq(gpio_to_irq(myirq_descs[i].irq_id), &myirq_descs[i]); printk("*******%s*****3***\n", __FUNCTION__); cdev_del(&myvirtualdisk->mycdev); printk("*******%s*****4***\n", __FUNCTION__); class_device_unregister(myvirtualdisk->mycdevclassdevice); printk("*******%s*****5***\n", __FUNCTION__); class_destroy(myvirtualdisk->mycdevclass); printk("*******%s*****6***\n", __FUNCTION__); kfree(myvirtualdisk); printk("*******%s********\n", __FUNCTION__); unregister_chrdev_region(MKDEV(major, 0), 1); printk("*******%s*****7***\n", __FUNCTION__); } module_init(cdevtest_init); module_exit(cdevtest_exit); MODULE_LICENSE("GPL");
完整的workqueue工作隊列中斷上下半部代碼
#include <linux/kernel.h> #include <linux/device.h> #include <linux/cdev.h> #include <linux/fs.h> #include <linux/err.h> #include <linux/irq.h> #include <linux/input.h> #include <linux/interrupt.h> #include <linux/timer.h> #include <linux/workqueue.h> #include <asm-arm/param.h> #include <asm/arch/regs-gpio.h> #include <asm/gpio.h> #define VIRTUAL_MAJOR 250 int major = VIRTUAL_MAJOR; struct myirq_desc{ int irq_id; char *irq_name; int irq_code; }; struct myirq_desc myirq_descs[3]= { {S3C2410_GPF0, "s2", KEY_J}, {S3C2410_GPF2, "s3", KEY_K}, {S3C2410_GPG3, "s4", KEY_Z}, }; struct VirtualDisk{ struct class *mycdevclass;//在/sys/class創建類 struct class_device *mycdevclassdevice;//在/dev下創建設備 struct cdev mycdev;//給設備添加相關的fileoption struct timer_list mytimer; struct tasklet_struct mytasklet; struct workqueue_struct *myworkqueue;//工作隊列 struct work_struct mywork;//工作;工作隊列中的一項工作 }; struct VirtualDisk *myvirtualdisk; static struct file_operations mydev_fops = { .owner = THIS_MODULE, }; static void mywork_func(struct work_struct *work){ printk("*******%s********\n", __FUNCTION__); } static void mytasklet_func(unsigned long fundata){ printk("*****%s******,date = %ld\n", __FUNCTION__, fundata); } static void mytimer_func(unsigned long fundata){ printk("*******%s********\n", __FUNCTION__); //struct VirtualDisk *myvirtualdisk_fun = (struct VirtualDisk *)(fundata); //myvirtualdisk_fun->mytimer.expires =jiffies + 2 * HZ; //add_timer(&myvirtualdisk_fun->mytimer); printk("timer func happened!\n"); } static irqreturn_t myirq_handle(int irq, void *dev_id){ struct myirq_desc *myirq_descone = (struct myirq_desc *)dev_id; printk("*******%s********\n", __FUNCTION__); printk("irq = %d, irq_id = %d,irq_name = %s, irq_code = %c\n", irq, myirq_descone->irq_id, myirq_descone->irq_name, myirq_descone->irq_code); queue_work(myvirtualdisk->myworkqueue, &myvirtualdisk->mywork); tasklet_schedule(&myvirtualdisk->mytasklet);//激發任務,將mytasklet_func加入系統任務 mod_timer(&myvirtualdisk->mytimer, jiffies + 2*HZ); return IRQ_RETVAL(IRQ_HANDLED); } static int __init cdevtest_init(void){ dev_t mydev = MKDEV(major, 0); int ret; int i = 0; printk("*******%s********\n", __FUNCTION__); if(major){//註冊proc/devices ret = register_chrdev_region(mydev, 1, "mynewdriver"); }else{ ret = alloc_chrdev_region(&mydev, 0, 1, "mynewdriver"); major = MAJOR(mydev); } if(ret < 0){ printk(KERN_ERR "register_chrdev_region failed!\n"); ret = -EINVAL; return ret; } myvirtualdisk = kmalloc(sizeof(struct VirtualDisk), GFP_KERNEL); if(!myvirtualdisk){ ret = -ENOMEM; printk(KERN_ERR "kmalloc myvirtualdisk failed!\n"); goto release_chrdev; } myvirtualdisk->mycdevclass = class_create(THIS_MODULE, "mynewdriver"); if(IS_ERR(myvirtualdisk->mycdevclass)){ ret = PTR_ERR(myvirtualdisk->mycdevclass); printk(KERN_ERR "class_create failed!\n"); goto release_mem_malloc; } myvirtualdisk->mycdevclassdevice = class_device_create(myvirtualdisk->mycdevclass, NULL, MKDEV(major, 0), NULL, "mynewdriver"); if(IS_ERR(myvirtualdisk->mycdevclassdevice)){ ret = PTR_ERR(myvirtualdisk->mycdevclassdevice); printk(KERN_ERR "class_device_create failed!\n"); goto release_class_create; } //工作和工作隊列 INIT_WORK(&myvirtualdisk->mywork, mywork_func); myvirtualdisk->myworkqueue = create_workqueue("myworkqueue"); if (!myvirtualdisk->myworkqueue) { ret = -ENOMEM; goto release_class_create; } cdev_init(&(myvirtualdisk->mycdev), &mydev_fops); myvirtualdisk->mycdev.owner = THIS_MODULE; ret = cdev_add(&myvirtualdisk->mycdev, MKDEV(major, 0), 1); //tasklet 任務調度 tasklet_init(&myvirtualdisk->mytasklet, mytasklet_func, (unsigned long) 90); //這裏把timer相關的放在irq前面,不然會有bug出現(當在insmod時候按下按鈕就會出錯) init_timer(&myvirtualdisk->mytimer); myvirtualdisk->mytimer.function = mytimer_func; myvirtualdisk->mytimer.data = (unsigned long)myvirtualdisk; if(ret < 0){ goto release_device_class_create; } for(i = 0; i < 3; i++) { ret = request_irq(gpio_to_irq(myirq_descs[i].irq_id), myirq_handle, IRQF_TRIGGER_FALLING, myirq_descs[i].irq_name, &myirq_descs[i]); if(ret < 0){ printk(KERN_ERR "request irq failed!\n"); ret =-EFAULT; goto release_cdevandtimer; } } /*在模塊註銷的時候,銷燬工作隊列和工作對象 void flush_workqueue(struct workqueue_struct * wq) 該函數會一直等待,知道指定的等待隊列中所有的任務都執行完畢並從等待隊列中移除。 void destroy_workqueue(struct workqueue_struct * wq); 該函數是是創建等待隊列的反操作,註銷掉指定的等待隊列。*/ return 0; release_cdevandtimer: tasklet_kill(&myvirtualdisk->mytasklet);//刪除任務 del_timer(&myvirtualdisk->mytimer); cdev_del(&myvirtualdisk->mycdev); /*在模塊註銷的時候,銷燬工作隊列和工作對象 void flush_workqueue(struct workqueue_struct * wq) 該函數會一直等待,知道指定的等待隊列中所有的任務都執行完畢並從等待隊列中移除。 void destroy_workqueue(struct workqueue_struct * wq); 該函數是是創建等待隊列的反操作,註銷掉指定的等待隊列。*/ flush_workqueue(myvirtualdisk->myworkqueue); destroy_workqueue(myvirtualdisk->myworkqueue); release_device_class_create: class_device_unregister(myvirtualdisk->mycdevclassdevice); release_class_create: class_destroy(myvirtualdisk->mycdevclass); release_mem_malloc: kfree(myvirtualdisk); release_chrdev: unregister_chrdev_region(MKDEV(major, 0), 1); return ret; } static void __exit cdevtest_exit(void){ int i = 0; printk("*******%s****1****\n", __FUNCTION__); tasklet_kill(&myvirtualdisk->mytasklet);//刪除任務 del_timer(&myvirtualdisk->mytimer); printk("*******%s*****2***\n", __FUNCTION__); for(i = 0; i < 3; i++)free_irq(gpio_to_irq(myirq_descs[i].irq_id), &myirq_descs[i]); printk("*******%s*****3***\n", __FUNCTION__); cdev_del(&myvirtualdisk->mycdev); //刪除工作和工作隊列 flush_workqueue(myvirtualdisk->myworkqueue); destroy_workqueue(myvirtualdisk->myworkqueue); printk("*******%s*****4***\n", __FUNCTION__); class_device_unregister(myvirtualdisk->mycdevclassdevice); printk("*******%s*****5***\n", __FUNCTION__); class_destroy(myvirtualdisk->mycdevclass); printk("*******%s*****6***\n", __FUNCTION__); kfree(myvirtualdisk); printk("*******%s********\n", __FUNCTION__); unregister_chrdev_region(MKDEV(major, 0), 1); printk("*******%s*****7***\n", __FUNCTION__); } module_init(cdevtest_init); module_exit(cdevtest_exit); MODULE_LICENSE("GPL");