高级字符设备驱动--中断下半部机制之workqueue(二)

工作队列workqueue

工作队列(work queue)是另外一种将中断的部分工作推后的一种方式,它可以实现一些tasklet不能实现的工作,比如工作队列机制可以睡眠。这种差异的本质原因是,在工作队列机制中,将推后的工作交给一个称之为工作者线程(worker thread)的内核线程去完成(单核下一般会交给默认的线程events/0)。因此,在该机制中,当内核在执行中断的剩余工作时就处在进程上下文(process context)中。也就是说由工作队列所执行的中断代码会表现出进程的一些特性,最典型的就是可以重新调度甚至睡眠。

对于tasklet机制(中断处理程序也是如此),内核在执行时处于中断上下文(interrupt context)中。而中断上下文与进程毫无瓜葛,所以在中断上下文中就不能睡眠。因此,当推后的那部分中断程序需要睡眠时,工作队列毫无疑问是最佳选择;否则用tasklet。

  • tasklet 在软件中断上下文中运行的结果是所有的 tasklet 代码必须是原子的. 相反, 工作队列函数在一个特殊内核进程上下文运行; 结果, 它们有更多的灵活性. 特别地, 工作队列函数能够睡眠.

  • tasklet 常常在它们最初被提交的处理器上运行. 工作队列以相同地方式工作, 缺省地.

  • 内核代码可以请求工作队列函数被延后一个明确的时间间隔.


工作队列的实现

工作队列work_struct结构体,位于/include/linux/workqueue.h

typedef void (*work_func_t)(struct work_struct *work);

struct work_struct {
      atomic_long_t data; /*传递给处理函数的参数*/
#define WORK_STRUCT_PENDING 0/*工作是否正在等待处理标志*/             
#define WORK_STRUCT_FLAG_MASK (3UL)
#define WORK_STRUCT_WQ_DATA_MASK (~WORK_STRUCT_FLAG_MASK)
      struct list_head entry;  /* 连接所有工作的链表*/
      work_func_t func; /* 要执行的函数*/
#ifdef CONFIG_LOCKDEP
      struct lockdep_map lockdep_map;
#endif
};

这些结构被连接成链表。当一个工作者线程被唤醒时,它会执行它的链表上的所有工作。工作被执行完毕,它就将相应的work_struct对象从链表上移去。当链表上不再有对象的时候,它就会继续休眠。可以通过DECLARE_WORK在编译时静态地创建该结构,以完成推后的工作。

工作的创建(静态方法)

#define DECLARE_WORK(n, f)                                 \
            struct work_struct n = __WORK_INITIALIZER(n, f)

而后边这个宏为一下内容:
#define __WORK_INITIALIZER(n, f) {                      \
      .data = WORK_DATA_INIT(),                            \
      .entry      = { &(n).entry, &(n).entry },                    \
      .func = (f),                                        \
      __WORK_INIT_LOCKDEP_MAP(#n, &(n))                   \
      }

其为参数data赋值的宏定义为:

#define WORK_DATA_INIT()       ATOMIC_LONG_INIT(0)

这样就会静态地创建一个名为n,待执行函数为f,参数为data的work_struct结构。

工作的创建(动态方法)

在运行时通过指针创建一个工作:

INIT_WORK(struct work_struct *work, void(*func) (void *));

这会动态地初始化一个由work指向的工作队列,并将其与处理函数绑定。宏原型为:


#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)

在需要调度的时候引用类似tasklet_schedule()函数的相应调度工作队列执行的函数schedule_work(),如:

schedule_work(&work);/*调度工作队列执行*/

如果有时候并不希望工作马上就被执行,而是希望它经过一段延迟以后再执行。在这种情况下,可以调度指定的时间后执行函数:

schedule_delayed_work(&work,delay);函数原型为:

int schedule_delayed_work(struct delayed_work *work, unsigned long delay);

其中是以delayed_work为结构体的指针,而这个结构体的定义是在work_struct结构体的基础上增加了一项timer_list结构体。

struct delayed_work {
    struct work_struct work;
    struct timer_list timer; /* 延迟的工作队列所用到的定时器,当不需要延迟时初始化为NULL*/
};

这样,便使预设的工作队列直到delay指定的时钟节拍用完以后才会执行。

工作队列workqueue

建立 work_struct 结构并初始化, 使用下面宏:
      INIT_WORK(struct work_struct *work, void (*function)(void *), void *data);

一个工作队列必须明确的在使用前创建,宏为:
struct workqueue_struct *create_workqueue(const char *name);

当用完一个工作队列,可以去掉它,使用:
     void destroy_workqueue(struct workqueue_struct *queue);

把任务(work_struct)加入到工作队列中
    
     int queue_work(struct workqueue_struct *queue, struct work_struct *work);
     int queue_delayed_work(struct workqueue_struct *queue,
                                                 struct work_struct *work, unsigned long delay);

    //delay是为了保证至少在经过一段给定的最小延迟时间以后,工作队列中的任务才可以真正执行

1. 在工作队列中等待了长时间也没有运行的任务可以用下面的方法取消:
    int cancel_delayed_work(struct work_struct *work);
2.清空工作队列中的所有任务使用:
    void flush_workqueue(struct workqueue_struct *queue);
3.销毁工作队列使用:
    void destroy_workqueue(struct workqueue_struct *queue);


4.向内核缺省工作队列中加入任务

int schedule_work(struct work_struct *work);
int schedule_delayed_work(struct work_struct *work, unsigned long delay);


模板

使用工作队列处理中断下半部的设备驱动程序模板如下:

/*定义工作队列和下半部函数并关联*/
struct work_struct my_wq;

void my_do_work(unsigned long);
/*中断处理下半部*/
void my_do_work(unsigned long)
{
  ……/*编写自己的处理事件内容*/
}

/*中断处理上半部*/
irpreturn_t my_interrupt(unsigned int irq,void *dev_id)
{
……
schedule_work(&my_wq)/*调度my_wq函数,根据工作队列初始化函数将去执行my_do_work函数*/
……
}

/*设备驱动的加载函数*/
int __init xxx_init(void)
{
……
/*申请中断,转去执行my_interrupt函数并传入参数*/
result=request_irq(my_irq,my_interrupt,IRQF_DISABLED,"xxx",NULL);
……
/*初始化工作队列函数,并与自定义处理函数关联*/
INIT_WORK(&my_irq,(void (*)(void *))my_do_work);
……
}

/*设备驱动模块的卸载函数*/
void __exit xxx_exit(void)
{
……
/*释放中断*/
free_irq(my_irq,my_interrupt);
……
}


实验代码:

  1. #include <linux/init.h> 
  2. #include <linux/module.h> 
  3. #include <linux/kernel.h> 
  4.  
  5. #include <linux/fs.h> 
  6. #include <asm/uaccess.h> 
  7.  
  8. #include <asm/io.h> 
  9. #include <mach/regs-gpio.h> 
  10. #include <linux/ioport.h> 
  11.  
  12. #include <linux/interrupt.h> 
  13.  
  14. #define MAJOR   251 
  15. static char drv_name[] ="interrupt_dev"
  16. static char kernel_buf[1024]; 
  17. char var; 
  18. #define key_irq1 IRQ_EINT7 
  19.  
  20. struct work_struct my_wq; 
  21. void my_do_tasklet(unsigned long); 
  22. DECLARE_TASKLET(my_tasklet,my_do_tasklet,0); 
  23. void my_do_tasklet(unsigned long data) 
  24.     printk("-----tasklet-----\n"); 
  25. void my_do_work(unsigned long data) 
  26.     printk("----work_queue-----\n"); 
  27.  
  28. static void led_init() 
  29.     printk("led_init!\n"); 
  30.     __raw_writel(((__raw_readl(S3C2410_GPBCON) & (~(0xff<<10))) | (0x55<<10)), S3C2410_GPBCON); 
  31.     __raw_writel(__raw_readl(S3C2410_GPBUP) | (0xf<<5), S3C2410_GPBUP);     
  32.     __raw_writel(__raw_readl(S3C2410_GPBDAT) | (0xf<<5), S3C2410_GPBDAT); 
  33. static void Key_init() 
  34.     printk("key_init\n"); 
  35.  
  36. //  __raw_writel(((__raw_readl(S3C2410_GPFCON) & (~(0xff<<0))) | (0x02<<14)), S3C2410_GPFCON);  
  37.  
  38.  
  39. static irqreturn_t irq_handle_key(void
  40.     printk("this is for a test!\n"); 
  41.     schedule_work(&my_wq); 
  42.     tasklet_schedule(&my_tasklet); 
  43.     return IRQ_RETVAL(IRQ_HANDLED); 
  44.      
  45.  
  46.  
  47. static int char_dev_open(struct inode *inode,struct file *file) 
  48.  
  49.     led_init(); 
  50.     Key_init(); 
  51.  
  52.     INIT_WORK(&my_wq,my_do_work); 
  53.     if(request_irq(key_irq1,irq_handle_key,IRQF_TRIGGER_FALLING,drv_name,NULL) == 0) 
  54.     { 
  55.         printk("request_irq success!\n"); 
  56.     }else
  57.         printk("request_irq fail!\n"); 
  58.         return -1; 
  59.     } 
  60.         return 0; 
  61.  
  62. static int char_dev_release(struct inode *inode,struct file *file) 
  63.         free_irq(key_irq1,NULL); 
  64.         printk("\n\nchar_dev_release success!"); 
  65.         return 0; 
  66.  
  67. static struct file_operations char_dev_fops = { 
  68.         .owner = THIS_MODULE, 
  69.         .open  = char_dev_open, 
  70.         .release = char_dev_release, 
  71. }; 
  72.  
  73. static int __init char_dev_init(void
  74.     printk("module init\n"); 
  75.     if(register_chrdev(MAJOR,drv_name,&char_dev_fops)<0)  
  76.     { 
  77.             printk("fail to register!\n"); 
  78.             return -1; 
  79.         } 
  80.         else 
  81.         printk("success to register!\n"); 
  82.  
  83.     return 0; 
  84.  
  85. static void __exit char_dev_exit(void
  86.     unregister_chrdev(MAJOR,drv_name); 
  87.     printk("module exit\n"); 
  88.  
  89. module_init(char_dev_init); 
  90. module_exit(char_dev_exit); 
  91.  
  92. MODULE_LICENSE("GPL"); 
  93. MODULE_AUTHOR("jianchi88"); 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值