工作佇列(workqueue)
workqueue很類似於tasklet,但是兩者在本質上有著一些重大差異:
tasklet是在軟體中斷環境下運作,所有的tasklet程式碼都必須符合連動的要件。相對的,workqueue可有較長的延遲時間,而且不必連動,甚至允許休眠。
工作佇列的型別是struct workqueue_struct,定義於<linux/workqueue.h>
,照例,必須先被初始化才能使用。核心提供兩個初始化工作佇列的函式:
struct workqueue_struct *create_workqueue(const char *name);
struct workqueue_struct *create_singlethread_workqueue(const char *name);
使用create_workqueue(),所獲得的工作佇列,對系統上的每個處理器各有一個專屬執行緒。使需要一個執行緒的話就改用create_singlethread_workqueue()。要將函式排入工作佇列必須填寫一個work_struct的結構。利用以下巨集,可於編譯期初始化work_struct結構:
DECLARE_WORK(name,void (*function)(void*),void *data);
其中的name是所要宣告的結構之名稱,function是要被排入工作佇列的函式,data是傳給該函式的參數值。若需要於執行期間設定work_struct結構,則應該改用下列兩個巨集:
INIT_WORK(struct work_struct *work,void (*function)(void*),void *data);
PREPARE_WORK(struct work_struct *work,void (*function)(void*)void *data);
PREPARE_WORK()所做的事情跟INIT_WORK()幾乎一樣,但它不是將工作排入工作佇列。 下列兩個函式可將工作函式排入佇列:
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);
兩個函式都可將work排入指定的queue,不過當你使用queue_delayed_work()時,至少經過delay個tick之後才會執行排入的work。 對於尚在等待的工作函式,可用下列方法來註銷:
int cancel_delayed_work(struct work_struct *work);
若能取消工作,也就是在開始執行之前就將工作函式的指標移出結構,上述函式會傳回一個非零值,也就是說,在cancel_delayed_work()返回之後,核心就絕對不會執行指定的工作函式。相對地,如果cancel_delayed_work()傳回0,表示該工作函式可能已在另一個不同的處理器上開工了,而且在cancel_delayed_work()返回之後仍可能繼續跑。若要絕對確定工作函式不會在系統上任何地方發揮作用,除了要確定cancel_delayed_work()傳回值為0之外,還必須呼叫下列函式:
void flush_work_queue(struct workqueue_struct *queue);
當flush_workqueue()返回時,在這之前所排入的工作就會全部失效。若工作佇列已經沒有利用價值則可使用下列函式銷毀:
void destroy_workqueue(struct workqueue_struct *queue);
公共工作佇列
實務上,裝置很少用到自己專用的工作佇列。因此使用公共工作佇列反而會比較有效率。 jiq(just in queue)模組釋出兩個檔案來示範公共工作佇列的用法。它們只使用了一個work_struct結構,期設定步驟如下:
static struct work_struct jiq_work;
INIT_WORK(&jig_work,jig_print_wq,&jiq_data);
用於排入工作的函式是:
int schedule_work(struct work_struct *work);