要使用completion,必須在檔案中包含<linux/completion.h>,同時建立一個類型為struct completion的變量。
這個變量可以靜态地聲明和初始化:
DECLARE_COMPLETION(my_comp);
或者動态初始化:
struct completion my_comp;
init_completion(&my_comp);
如果驅動程式要在執行後面操作之前等待某個過程的完成,它可以調用wait_for_completion ,以要完成的事件為參數:
void wait_for_completion(struct completion *comp);
wait_for_completion等待在completion上。如果加了interruptible,就表示線程等待可被外部發來的信号打斷;如果加了killable,就表示線程隻可被kill信号打斷;如果加了timeout,表示等待超出一定時間會自動結束等待,timeout的機關是系統所用的時間片jiffies(多為1ms)。
如果其它部分代碼可以确定事件已經完成,可以調用下面兩個函數之一來喚醒等待該事件的程序:
void complete(struct completion *comp);
void complete_all(struct completion *comp); /* Linux 2.5.x以上版本 */
前一個函數将隻喚醒一個等待程序,而後一個函數喚醒等待該事件的是以程序。由于completion的實作方式,即使complete在wait_for_competion之前調用,也可以正常工作。
例如,在MD裝置驅動程式實作中,有一個恢複線程md_recovery_thread。驅動程式通過md_register_thread和md_unregister_thread來注冊和登出恢複線程。恢複線程的執行邏輯在md_thread函數中,大緻如下:
int md_thread(void * arg)
{
線程初始化;
while (運作) {
處理邏輯;
接收信号;
}
return 0;
}
md_register_thread将建立一個恢複線程,它必須線上程真正初始化結束之後才能傳回該線程的指針。是以,其邏輯是:
mdk_thread_t *md_register_thread(void (*run) (void *), void *data, const char *name)
mdk_thread_t *thread;
……
struct completion event;
/* 為線程配置設定空間 */
thread = (mdk_thread_t *) kmalloc (sizeof(mdk_thread_t), GFP_KERNEL);
init_completion(&event);
thread->event = &event;
/* 建立核心線程 */
ret = kernel_thread(md_thread, thread, 0);
/* 等待線程初始化結束 */
wait_for_completion(&event);
/* 傳回線程指針 */
return thread;
而md_unregister_thread通過向線程發送SIGKILL信号登出恢複線程,它也需要線上程真正退出後才能釋放線程所占用的記憶體。是以,其邏輯是:
void md_unregister_thread(mdk_thread_t *thread)
/* 向線程發送SIGKILL信号終止其運作 */
md_interrupt_thread(thread);
/* 等待線程退出 */
/* 釋放線程所占用的記憶體 */
kfree(thread);
如果考慮completion,md_thread的邏輯是:
complete(thread->event);
需要說明的是,由于等待事件是在驅動程式和恢複線程中的一個共享資源,它必須是一個全局變量,或者如實作代碼中,定義為一個局部變量,而将其指針放在恢複線程結構中。
typedef struct mdk_thread_s {
……
struct completion *event;
} mdk_thread_t;
【新浪微網誌】 張昺華--sky
【twitter】 @sky2030_
【facebook】 張昺華 zhangbinghua
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利.