本文部分参考华清远见文档

中断上半部要求执行时间间隔段,所以往往将处理时间较长的代码放在中断下半部来处理

中断下半部的应用:网卡驱动上半部初始化网卡驱动等短时间的事件,下半部收发数据

中断下半部:

    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");