在Linux內核中,completion是一種簡單的同步機制,標誌"things may proceed"。要使用completion,必須在文件中包含<linux/completion.h>,同時創建一個類型爲 structcompletion的變量。這個變量可以靜態地聲明和初始化:
DECLARE_COMPLETION(my_comp);
或者動態初始化:
struct completionmy_comp;
init_completion(&my_comp);
如果驅動程序要在執行後面操作之前等待某個過程的完成,它可以調用wait_for_completion ,以要完成的事件爲參數:
void wait_for_completion(struct completion *comp);
如果其它部分代碼可以確定事件已經完成,可以調用下面兩個函數之一來喚醒等待該事件的進程:
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)
{
struct completion event;
init_completion(&event);
thread->event = &event;
……
/* 向線程發送SIGKILL信號終止其運行 */
md_interrupt_thread(thread);
/* 等待線程退出 */
wait_for_completion(&event);
/* 釋放線程所佔用的內存 */
kfree(thread);
}
如果考慮completion,md_thread的邏輯是:
int md_thread(void * arg)
{
線程初始化;
complete(thread->event);
while (運行) {
處理邏輯;
接收信號;
}
complete(thread->event);
return 0;
}
需要說明的是,由於等待事件是在驅動程序和恢復線程中的一個共享資源,它必須是一個全局變量,或者如實現代碼中,定義爲一個局部變量,而將其指針放在恢復線程結構中。
typedef struct mdk_thread_s {
……
struct completion *event;
……
} mdk_thread_t;