天天看點

Linux中的工作隊列I、2.6.0~2.6.19II、2.6.20~2.6.??

工作隊列(work queue)是Linux kernel中将工作推後執行的一種機制。這種機制和BH或Tasklets不同之處在于工作隊列是把推後的工作交由一個核心線程去執行,是以工作隊列的優勢就在于它允許重新排程甚至睡眠。

工作隊列是2.6核心開始引入的機制,在2.6.20之後,工作隊列的資料結構發生了一些變化,是以本文分成兩個部分對2.6.20之前和之後的版本分别做介紹。

I、2.6.0~2.6.19

資料結構:

struct

work_struct {

unsigned

long

pending;

struct

list_head entry;

void

(*func)(

void

*);

void

*data;

void

*wq_data;

struct

timer_list timer;

};

pending是用來記錄工作是否已經挂在隊列上;

entry是循環連結清單結構;

func作為函數指針,由使用者實作;

data用來存儲使用者的私人資料,此資料即是func的參數;

wq_data一般用來指向工作者線程(工作者線程參考下文);

timer是推後執行的定時器。

work_struct的這些變量裡,func和data是使用者使用的,其他是内部變量,我們可以不用太過關心。

API:

INIT_WORK(_work, _func, _data);

int

schedule_work(

struct

work_struct *work);

int

schedule_delayed_work(

struct

work_struct *work, unsigned

long

delay);

void

flush_scheduled_work(

void

);

int

cancel_delayed_work(

struct

work_struct *work);

1、初始化指定工作,目的是把使用者指定的函數_func及_func需要的參數_data賦給work_struct的func及data變量。

2、對工作進行排程,即把給定工作的處理函數送出給預設的工作隊列和工作者線程。工作者線程本質上是一個普通的核心線程,在預設情況下,每個CPU均有一個類型為“events”的工作者線程,當調用schedule_work時,這個工作者線程會被喚醒去執行工作連結清單上的所有工作。

3、延遲執行工作,與schedule_work類似。

4、重新整理預設工作隊列。此函數會一直等待,直到隊列中的所有工作都被執行。

5、flush_scheduled_work并不取消任何延遲執行的工作,是以,如果要取消延遲工作,應該調用cancel_delayed_work。

以上均是采用預設工作者線程來實作工作隊列,其優點是簡單易用,缺點是如果預設工作隊列負載太重,執行效率會很低,這就需要我們建立自己的工作者線程和工作隊列。

struct

workqueue_struct *create_workqueue(

const

char

*name);

int

queue_work(

struct

workqueue_struct *wq,

struct

work_struct *work);

int

queue_delayed_work(

struct

workqueue_struct *wq,

struct

work_struct *work, unsigned

long

delay);

void

flush_workqueue(

struct

workqueue_struct *wq);

void

destroy_workqueue(

struct

workqueue_struct *wq);

1、建立新的工作隊列和相應的工作者線程,name用于該核心線程的命名。

2、類似于schedule_work,差別在于queue_work把給定工作送出給建立的工作隊列wq而不是預設隊列。

3、延遲執行工作。

4、重新整理指定工作隊列。

5、釋放建立的工作隊列。

下面一段代碼可以看作一個簡單的實作:

void

my_func(

void

*data)

{

char

*name = (

char

*)data;

printk(KERN_INFO “Hello world, my name is %s!\n”, name);

}

struct

workqueue_struct *my_wq = create_workqueue(“my wq”);

struct

work_struct my_work;

INIT_WORK(&my_work, my_func, “Jack”);

queue_work(my_wq, &my_work);

destroy_workqueue(my_wq);

II、2.6.20~2.6.??

自2.6.20起,工作隊列的資料結構發生了一些變化,使用時不能沿用舊的方法。

typedef

void

(*work_func_t)(

struct

work_struct *work);

struct

work_struct {

atomic_long_t data;

struct

list_head entry;

work_func_t func;

};

與2.6.19之前的版本相比,work_struct瘦身不少。粗粗一看,entry和之前的版本相同,func和data發生了變化,另外并無其他的變量。

entry我們不去過問,這個和以前的版本完全相同。data的類型是atomic_long_t,這個類型從字面上看可以知道是一個原子類型。第一次看到這個變量時,很容易誤認為和以前的data是同樣的用法,隻不過類型變了而已,其實不然,這裡的data是之前版本的pending和wq_data的複合體,起到了以前的pending和wq_data的作用。

func的參數是一個work_struct指針,指向的資料就是定義func的work_struct。

看到這裡,會有兩個疑問,第一,如何把使用者的資料作為參數傳遞給func呢?以前有void *data來作為參數,現在好像完全沒有辦法做到;第二,如何實作延遲工作?目前版本的work_struct并沒有定義timer。

解決第一個問題,需要換一種思路。2.6.20版本之後使用工作隊列需要把work_struct定義在使用者的資料結構中,然後通過container_of來得到使用者資料。具體用法可以參考稍後的實作。

對于第二個問題,新的工作隊列把timer拿掉的用意是使得work_struct更加單純。首先回憶一下之前版本,隻有在需要延遲執行工作時才會用到timer,普通情況下timer是沒有意義的,是以之前的做法在一定程度上有些浪費資源。是以新版本中,将timer從work_struct中拿掉,然後又定義了一個新的結構delayed_work用于處理延遲執行:

struct

delayed_work {

struct

work_struct work;

struct

timer_list timer;

};

下面把API羅列一下,每個函數的解釋可參考之前版本的介紹或者之後的實作:

INIT_WORK(

struct

work_struct *work, work_func_t func);

INIT_DELAYED_WORK(

struct

delayed_work *work, work_func_t func);

int

schedule_work(

struct

work_struct *work);

int

schedule_delayed_work(

struct

delayed_work *work, unsigned

long

delay);

struct

workqueue_struct *create_workqueue(

const

char

*name);

int

queue_work(

struct

workqueue_struct *wq,

struct

work_struct *work);

int

queue_delayed_work(

struct

workqueue_struct *wq,

struct

delayed_work *work, unsigned

long

delay);

void

flush_scheduled_work(

void

);

void

flush_workqueue(

struct

workqueue_struct *wq);

int

cancel_delayed_work(

struct

delayed_work *work);

void

destroy_workqueue(

struct

workqueue_struct *wq);

其中,1、2、4、7和以前略有差別,其他用法完全一樣。

實作:

struct

my_struct_t {

char

*name;

struct

work_struct my_work;

};

void

my_func(

struct

work_struct *work)

{

struct

my_struct_t *my_name = container_of(work,

struct

my_struct_t, my_work);

printk(KERN_INFO “Hello world, my name is %s!\n”, my_name->name);

}

struct

workqueue_struct *my_wq = create_workqueue(“my wq”);

struct

my_struct_t my_name;

my_name.name = “Jack”;

INIT_WORK(&(my_name.my_work), my_func);

queue_work(my_wq, &(my_name.my_work));

destroy_workqueue(my_wq);

作者:wwang 

出處:http://www.cnblogs.com/wwang 

本文采用

知識共享署名-非商業性使用-相同方式共享 2.5 中國大陸許可協定

進行許可,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接。

--------懶人評論(請勿重複點選)--------

繼續閱讀