內核完成變量completion的原理

一結構體變量定義:

 25 struct completion {
 26         unsigned int done; //決定進程是否睡眠等待
 27         wait_queue_head_t wait; //進程在此睡眠等待
 28 };

  二相關函數:

          睡眠等待: 

91 extern void wait_for_completion(struct completion *);
92 extern void wait_for_completion_io(struct completion *);
93 extern int wait_for_completion_interruptible(struct completion *x);
94 extern int wait_for_completion_killable(struct completion *x);
95 extern unsigned long wait_for_completion_timeout(struct completion *x,
96 unsigned long timeout);
97 extern unsigned long wait_for_completion_io_timeout(struct completion *x,
98 unsigned long timeout);
99 extern long wait_for_completion_interruptible_timeout(
100 struct completion *x, unsigned long timeout);
101 extern long wait_for_completion_killable_timeout(
102 struct completion *x, unsigned long timeout);


以上函數實際都會調用wait_for_common()--->do_wait_for_common()

喚醒:

106 extern void complete(struct completion *); //completion->done +1,一次只滿足一個進程

107 extern void complete_all(struct completion *);//completion->done增幅很大, 進程不會再進入睡眠.

    *  以上函數實際都會調用__wake_up_common()

三流程圖                                                                       

不管是執行complete(x)還是wait_for_completion(x)都必須要搶佔x->wait.lock.  因此可能出現這種情況:x->done爲0.當進程A執行wait_for_completion(x)後等待x而進入睡眠。進程B執行了complete()後喚醒了進程A。但是在進程A搶佔x->wait.lock前, 同樣執行wait_for_completion(x)的進程C先搶佔到了鎖。獲得x->done後離開,x->done再次變爲0。而當A獲得鎖檢查x->done已經爲0, A 又再次進入睡眠.

四:類比

* 完成變量x好比是一個大倉庫. x->done是倉庫裏的記貨員。 

*執行wait_for_completion()的進程爲提貨人。

* 執行complete()的進程爲送貨人。

 *倉庫有鎖,一次只能進一個人,出倉庫時鑰匙掛門口。

 * 提貨人和送貨人來的順序不可知。 一件貨只能一個人提。

  1)當是提貨A人先來到倉庫, 他取下鑰匙,進了倉庫鎖上大門。接着詢問記貨員有沒有貨, 如果有貨,他立刻提貨走人。如果沒貨,他就離開倉庫還回鑰匙,到倉庫合營的酒店睡覺。

  2)當送貨人B來到倉庫, 取消鑰匙進了倉庫,存好貨物。記貨員登記以後, 便打電話(或廣播)通知在酒店裏睡覺的提貨人,如果有的話。送貨人換回鑰匙就離開了。

  3)提貨人聽到電話(或廣播)便立即趕到倉庫提貨, 提到貨和記貨員登記便執行離開。但是因爲酒店和倉庫有點遠, 在提貨人A趕到倉庫時,很可能有提貨人C已經提走了貨. 

擴展:

  1) 提貨人A可能攤上大事要趕火車, 所以他只能等timeout小時.如果這時間內沒貨, 他就攜款潛逃了。wait_for_completion_timeout(); 

  2) 提貨人A還有其他任務, 如果但他接到老闆的電話還沒有領到貨, 他也不管了.。 wait_for_completion_interruptible();

      還有可能這個電話是用來引爆A身上的定時炸彈。。。。 wait_for_completion_killable()

  3)  送貨人B送的貨太多,足夠所有的提貨人來提取。                complete_all()

五小技巧:

假設驅動模塊使用了完成變量,在驅動移除時因爲有進程在等待完成變量而進入睡眠導致無法移除。

可嘗試

   a).cat /proc/kallsyms | grep 'completion_name'若completion_name   0xfb002238寫簡單的模塊:

   b)

  1. static int __init complete_init(void)

  2. {

  3. complete((struct completion*)0xfb002238);

  4. }

c)加載模塊以完成完成變量