高級字符設備驅動--中斷下半部機制之workqueue(二)


 
工作隊列workqueue
工作隊列(work queue)是另外一種將中斷的部分工作推後的一種方式,它可以實現一些tasklet不能實現的工作,比如工作隊列機制可以睡眠。這種差異的本質原因是,在工作隊列機制中,將推後的工作交給一個稱之爲工作者線程(worker thread)的內核線程去完成(單核下一般會交給默認的線程events/0)。因此,在該機制中,當內核在執行中斷的剩餘工作時就處在進程上下文(process context)中。也就是說由工作隊列所執行的中斷代碼會表現出進程的一些特性,最典型的就是可以重新調度甚至睡眠。
對於tasklet機制(中斷處理程序也是如此),內核在執行時處於中斷上下文(interrupt context)中。而中斷上下文與進程毫無瓜葛,所以在中斷上下文中就不能睡眠。
因此,當推後的那部分中斷程序需要睡眠時,工作隊列毫無疑問是最佳選擇;否則用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);
……
}

實驗代碼:
#include <linux/init.h>  
#include <linux/module.h>  
#include <linux/kernel.h>  
  
#include <linux/fs.h>  
#include <asm/uaccess.h>  
  
#include <asm/io.h>  
#include <mach/regs-gpio.h>  
#include <linux/ioport.h>  
  
#include <linux/interrupt.h>  
  
#define MAJOR   251  
static char drv_name[] ="interrupt_dev";  
static char kernel_buf[1024];  
char var;  
#define key_irq1 IRQ_EINT7  
  
struct work_struct my_wq;  
void my_do_tasklet(unsigned long);  
DECLARE_TASKLET(my_tasklet,my_do_tasklet,0);  
void my_do_tasklet(unsigned long data)  
{  
    printk("-----tasklet-----\n");  
}  
void my_do_work(unsigned long data)  
{  
    printk("----work_queue-----\n");  
}  
  
static void led_init()  
{  
    printk("led_init!\n");  
    __raw_writel(((__raw_readl(S3C2410_GPBCON) & (~(0xff<<10))) | (0x55<<10)), S3C2410_GPBCON);  
    __raw_writel(__raw_readl(S3C2410_GPBUP) | (0xf<<5), S3C2410_GPBUP);      
    __raw_writel(__raw_readl(S3C2410_GPBDAT) | (0xf<<5), S3C2410_GPBDAT);  
}  
static void Key_init()  
{  
    printk("key_init\n");  
  
//  __raw_writel(((__raw_readl(S3C2410_GPFCON) & (~(0xff<<0))) | (0x02<<14)), S3C2410_GPFCON);   
  
}  
  
static irqreturn_t irq_handle_key(void)  
{  
    printk("this is for a test!\n");  
    schedule_work(&my_wq);  
    tasklet_schedule(&my_tasklet);  
    return IRQ_RETVAL(IRQ_HANDLED);  
      
}  
  
  
static int char_dev_open(struct inode *inode,struct file *file)  
{  
  
    led_init();  
    Key_init();  
  
    INIT_WORK(&my_wq,my_do_work);  
    if(request_irq(key_irq1,irq_handle_key,IRQF_TRIGGER_FALLING,drv_name,NULL) == 0)  
    {  
        printk("request_irq success!\n");  
    }else{  
        printk("request_irq fail!\n");  
        return -1;  
    }  
        return 0;  
}  
  
static int char_dev_release(struct inode *inode,struct file *file)  
{  
        free_irq(key_irq1,NULL);  
        printk("\n\nchar_dev_release success!");  
        return 0;  
}  
  
static struct file_operations char_dev_fops = {  
        .owner = THIS_MODULE,  
        .open  = char_dev_open,  
        .release = char_dev_release,  
};  
  
static int __init char_dev_init(void)  
{  
    printk("module init\n");  
    if(register_chrdev(MAJOR,drv_name,&char_dev_fops)<0)   
    {  
            printk("fail to register!\n");  
            return -1;  
        }  
        else  
        printk("success to register!\n");  
  
    return 0;  
}  
  
static void __exit char_dev_exit(void)  
{  
    unregister_chrdev(MAJOR,drv_name);  
    printk("module exit\n");  
}  
  
module_init(char_dev_init);  
module_exit(char_dev_exit);  
  
MODULE_LICENSE("GPL");  
MODULE_AUTHOR("jianchi88");  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章