W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
內(nèi)核編程的一個普通模式包括在當前線程之外初始化某個動作, 接著等待這個動作結束. 這個動作可能是創(chuàng)建一個新內(nèi)核線程或者用戶空間進程, 對一個存在著的進程的請求, 或者一些基于硬件的動作. 在這些情況中, 很有誘惑去使用一個旗標來同步 2 個任務, 使用這樣的代碼:
struct semaphore sem;
init_MUTEX_LOCKED(&sem);
start_external_task(&sem);
down(&sem);
外部任務可以接著調(diào)用 up(??sem), 在它的工作完成時.
事實證明, 這種情況旗標不是最好的工具. 正常使用中, 試圖加鎖一個旗標的代碼發(fā)現(xiàn)旗標幾乎在所有時間都可用; 如果對旗標有很多競爭, 性能會受損并且加鎖方案需要重新審視. 因此旗標已經(jīng)對"可用"情況做了很多的優(yōu)化. 當用上面展示的方法來通知任務完成, 然而, 調(diào)用 down 的線程將幾乎是一直不得不等待; 因此性能將受損. 旗標還可能易于處于一個( 困難的 ) 競爭情況, 如果它們表明為自動變量以這種方式使用時. 在一些情況中, 旗標可能在調(diào)用 up 的進程用完它之前消失.
這些問題引起了在 2.4.7 內(nèi)核中增加了 "completion" 接口. completion 是任務使用的一個輕量級機制: 允許一個線程告訴另一個線程工作已經(jīng)完成. 為使用 completion, 你的代碼必須包含 <linux/completion.h>. 一個 completion 可被創(chuàng)建, 使用:
DECLARE_COMPLETION(my_completion);
或者, 如果 completion 必須動態(tài)創(chuàng)建和初始化:
struct completion my_completion;
/* ... */
init_completion(&my_completion);
等待 completion 是一個簡單事來調(diào)用:
void wait_for_completion(struct completion *c);
注意這個函數(shù)進行一個不可打斷的等待. 如果你的代碼調(diào)用 wait_for_completion 并且沒有人完成這個任務, 結果會是一個不可殺死的進程.[18]
另一方面, 真正的 completion 事件可能通過調(diào)用下列之一來發(fā)出:
void complete(struct completion *c);
void complete_all(struct completion *c);
如果多于一個線程在等待同一個 completion 事件, 這 2 個函數(shù)做法不同. complete 只喚醒一個等待的線程, 而 complete_all 允許它們所有都繼續(xù). 在大部分情況下, 只有一個等待者, 這 2 個函數(shù)將產(chǎn)生一致的結果.
一個 completion 正常地是一個單發(fā)設備; 使用一次就放棄. 然而, 如果采取正確的措施重新使用 completion 結構是可能的. 如果沒有使用 complete_all, 重新使用一個 completion 結構沒有任何問題, 只要對于發(fā)出什么事件沒有模糊. 如果你使用 complete_all, 然而, 你必須在重新使用前重新初始化 completion 結構. 宏定義:
INIT_COMPLETION(struct completion c);
可用來快速進行這個初始化.
作為如何使用 completion 的一個例子, 考慮 complete 模塊, 它包含在例子源碼里. 這個模塊使用簡單的語義定義一個設備: 任何試圖從一個設備讀的進程將等待(使用 wait_for_completion)直到其他進程向這個設備寫. 實現(xiàn)這個行為的代碼是:
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 機制的典型使用是在模塊退出時與內(nèi)核線程的終止一起. 在這個原型例子里, 一些驅動的內(nèi)部工作是通過一個內(nèi)核線程在一個 while(1) 循環(huán)中進行的. 當模塊準備好被清理時, exit 函數(shù)告知線程退出并且等待結束. 為此目的, 內(nèi)核包含一個特殊的函數(shù)給線程使用:
void complete_and_exit(struct completion *c, long retval);
[18] 在本書編寫時, 添加可中斷版本的補丁已經(jīng)流行但是還沒有合并到主線中.
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: