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