本文檔的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()創建返回。