歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

如何使用Linux工作隊列workqueue

本文檔的Copyleft歸rosetta所有,使用GPL發布,可以自由拷貝、轉載,轉載時請保持文檔的完整性。

參考資料:《Linux設備驅動程序》第3版 LDD3e, LKD3e, 《 Linux per-CPU實現分析 》,linux-2.6.27,irq_balance

要使用workqueue當然逃不了per-CPU,per-CPU顧名思義,每個CPU,很多地方直接翻譯為“每CPU"。關於per-CPU的接口操作就是對於所有CPU的操作,即每個CPU都有一個數據副本或者線程等,如果這個接口傳入指定CPU編號,那麼就是對某個指定的CPU操作。具體的per-CPU內核實現請看之前轉載的文章《Linux per-CPU實現分析 》見 http://www.linuxidc.com/Linux/2012-06/62633.htm
       
1,創建一個per-CPU
        *編譯期間靜態創建一個per-CPU      
        DEFINE_PER_CPU(type, name) 
        創建一個名為name,數據類型為type的per-CPU,比如static DEFINE_PER_CPU(struct sk_buff_head, bs_cpu_queues),此時每個CPU都有一個名叫bs_cpu_queues,數據結構為sk_buff_head的變量副本。每個副本都是在自己的CPU上工作。
     
      * 動態創建per-CPU,以下代碼是內核create_workqueue實現的片斷
        struct workqueue_struct *__create_workqueue(const char *name,
                        int singlethread)
        {
            int cpu, destroy = 0;
            struct workqueue_struct *wq;
            struct task_struct *p;
       
            wq = kzalloc(sizeof(*wq), GFP_KERNEL);
            if (!wq)
                return NULL;
       
            wq->cpu_wq = alloc_percpu(struct cpu_workqueue_struct);
            if (!wq->cpu_wq) {
                kfree(wq);
                return NULL;
                 }
                ……
        }

2,使用工作隊列
        *編譯時靜態創建,因為暫時用不到也就沒去看具體實現過程和用法。
        #define DECLARE_WORK(name, void(*func)(void *), void *data);
        其使用的默認處理函數為work_handler(void *data)。
        使用schedule_work(&work)進行工作調度。
        *動態創建
        INIT_WORK(struct work_struct *work,void(*func)(void *), void *data);
        比如如下代碼
        static DEFINE_PER_CPU(struct sk_buff_head, bs_cpu_queues);
        int netif_receive_skb(struct sk_buff *skb)//接收到網卡數據
        {
                ……
                ……
                static inline int bs_dispatch(struct sk_buff *skb)//分發網卡數據
                {
                         struct sk_buff_head *q;
                        q = &per_cpu(bs_cpu_queues, cpu);//從CPU為cpu處取一個sk_buff_head數據結構
                        //把數據skb插入到雙向循環鏈表q中。這樣子這個q就是待處理的數據了。

                        bs_works = &per_cpu(bs_works, cpu);//從CPU為cpu處取一個work_struct結構
                        if (!bs_works->func) {//假如當前工作處理函數指針為空
                                 INIT_WORK(bs_works, bs_func, q);//創建工作隊列,工作隊列函數指為bs_func,處理的數據為q。
                                 queue_work(per_cpu_ptr(keventd_wq->cpu_wq, cpu), bs_works);//在CPU編號為cpu的默認隊列keventd_wq中插入一個bs_works工作任務。具體看下面。                         
                        }
                 }
                ……
                ……
        } 

3,創建新的工作隊列已經在《Linux工作隊列workqueue實現分析》講過了,是通過create_workqueue實現,這裡不再重復。

4,工作調度
        *默認工作隊列處理函數
        void work_handler(void *data)//在好幾個版本的內核裡沒有發現這個函數或者宏定義,只找到了work_handlers,它其實是個函數指針數組,具體實現沒仔細看,大概就是為初始化一些函數,再看時機進行調度。
        使用schedule_work()對默認的event隊列進程調度。
        *調度新創建的工作隊列
        int queue_work(struct workqueue_struct *wq, struct work_struct *work)
        從其參數可知,它是對某種類型的任務進行工作調度,即這種類型的每個CPU中的工作者線程的每個體work_struct都要被調度。
        由於上面使用的是per_cpu_ptr(keventd_wq->cpu_wq, cpu),其返回的是CPU為cpu的keventd_wq->cpu_wq workqueue_struct結構,即默認的工作隊列,所以上面其實可以使用schedule_work(&work)進行調度。使用queue_work一般是自己指定自行創建的工作隊列wq,這個工作隊列由 create_workqueue()創建返回。

Copyright © Linux教程網 All Rights Reserved