linux Completions 機制

內核編程的一個普通模式包括在當前線程以外初始化某個動做, 接着等待這個動做結束. 這個動做多是建立一個新內核線程或者用戶空間進程, 對一個存在着的進程的請求, 或 者一些基於硬件的動做. 在這些狀況中, 頗有誘惑去使用一個旗標來同步 2 個任務, 使 用這樣的代碼:html

 

struct semaphore sem; init_MUTEX_LOCKED(&sem); start_external_task(&sem); down(&sem);linux

 

外部任務能夠接着調用 up(??sem), 在它的工做完成時.編程

 

事實證實, 這種狀況旗標不是最好的工具. 正常使用中, 試圖加鎖一個旗標的代碼發現旗 標幾乎在全部時間均可用; 若是對旗標有不少競爭, 性能會受損而且加鎖方案須要從新審 視. 所以旗標已經對"可用"狀況作了不少的優化. 當用上面展現的方法來通知任務完成, 然而, 調用 down 的線程將幾乎是一直不得不等待; 所以性能將受損. 旗標還可能易於處 於一個( 困難的 ) 競爭狀況, 若是它們代表爲自動變量以這種方式使用時. 在一些狀況 中, 旗標可能在調用 up 的進程用完它以前消失.ide

 

這些問題引發了在 2.4.7 內核中增長了 "completion" 接口. completion 是任務使用的 一個輕量級機制: 容許一個線程告訴另外一個線程工做已經完成. 爲使用 completion, 你 的代碼必須包含 <linux/completion.h>. 一個 completion 可被建立, 使用:函數

 

DECLARE_COMPLETION(my_completion);工具

 

或者, 若是 completion 必須動態建立和初始化: struct completion my_completion;            /* ... */性能

 

init_completion(&my_completion); 等待 completion 是一個簡單事來調用:優化

void wait_for_completion(struct completion *c);線程

 

注意這個函數進行一個不可打斷的等待. 若是你的代碼調用 wait_for_completion 而且 沒有人完成這個任務, 結果會是一個不可殺死的進程.[18]18htm

另外一方面, 真正的 completion 事件可能經過調用下列之一來發出: void complete(struct completion *c);

void complete_all(struct completion *c);

 

若是多於一個線程在等待同一個 completion 事件, 這 2 個函數作法不一樣. complete 只 喚醒一個等待的線程, 而 complete_all 容許它們全部都繼續. 在大部分狀況下, 只有一 個等待者, 這 2 個函數將產生一致的結果.

 

一個 completion 正常地是一個單發設備; 使用一次就放棄. 然而, 若是採起正確的措施 從新使用 completion 結構是可能的. 若是沒有使用 complete_all, 從新使用一個 completion 結構沒有任何問題, 只要對於發出什麼事件沒有模糊. 若是你使用 complete_all, 然而, 你必須在從新使用前從新初始化 completion 結構. 宏定義:

 

INIT_COMPLETION(struct completion c); 可用來快速進行這個初始化.

做爲如何使用 completion 的一個例子, 考慮 complete 模塊, 它包含在例子源碼裏. 這 個模塊使用簡單的語義定義一個設備: 任何試圖從一個設備讀的進程將等待(使用 wait_for_completion)直到其餘進程向這個設備寫. 實現這個行爲的代碼是:

 

DECLARE_COMPLETION(comp);

ssize_t complete_read (struct file *filp, char user *buf, size_t count, loff_t *pos)

{

printk(KERN_DEBUG "process %i (%s) going to sleep\n",current->pid, current->comm);

wait_for_completion(&comp);

printk(KERN_DEBUG "awoken %i (%s)\n", current->pid, current->comm); return 0; /* EOF */

}

 

ssize_t complete_write (struct file *filp, const char user *buf, size_t count, loff_t *pos)

 

在本書編寫時, 添加可中斷版本的補丁已經流行可是尚未合併到主線中.

 

 

printk(KERN_DEBUG "process %i (%s) awakening the readers...\n", current-

>pid, current->comm); complete(&comp);

return count; /* succeed, to avoid retrial */

}

 

有多個進程同時從這個設備"讀"是有可能的. 每一個對設備的寫將確切地使一個讀操做完成, 可是沒有辦法知道會是哪一個.

 

completion 機制的典型使用是在模塊退出時與內核線程的終止一塊兒. 在這個原型例子裏, 一些驅動的內部工做是經過一個內核線程在一個 while(1) 循環中進行的. 當模塊準備好 被清理時, exit 函數告知線程退出而且等待結束. 爲此目的, 內核包含一個特殊的函數 給線程使用:

 

void complete_and_exit(struct completion *c, long retval);

相關文章
相關標籤/搜索