Linux設備驅動程序 之 完成量


內核編程中常見的一種模式是,在當前線程之外初始化某個活動,然后等待該活動的結束;這個活動可能是,創建一個新的內核線程或者新的用戶空間進程、對一個已有進程的某個請求,或者某種類型的硬件動作等;

內核提供了完成量(completion)來完成上述需求;完成量是一個輕量級的機制,它允許一個線程告訴另一個線程某個工作已經完成;為了使用完成量,代碼需要包含<linux/completion.h>;可以利用下面的宏靜態的創建和初始化完成量;

1 #define DECLARE_COMPLETION(work)

或者使用下面的方法動態的創建和初始化完成量;

1 struct completion my_completion;
2 /* 初始化函數 */
3 static inline void init_completion(struct completion *x)

需要等待完成,可以調用下面的方法,這些方法都以wait_for_completion開頭,區別在於比如是否可以打斷,是否提供超時等;

 1 extern void wait_for_completion(struct completion *);
 2 extern void wait_for_completion_io(struct completion *);
 3 extern int wait_for_completion_interruptible(struct completion *x);
 4 extern int wait_for_completion_killable(struct completion *x);
 5 extern unsigned long wait_for_completion_timeout(struct completion *x,
 6                            unsigned long timeout);
 7 extern unsigned long wait_for_completion_io_timeout(struct completion *x,
 8                             unsigned long timeout);
 9 extern long wait_for_completion_interruptible_timeout(
10     struct completion *x, unsigned long timeout);
11 extern long wait_for_completion_killable_timeout(
12     struct completion *x, unsigned long timeout);
13 extern bool try_wait_for_completion(struct completion *x);

實際的完成事件觸發則通過調用下面函數之一來完成;

1 extern void complete(struct completion *);
2 extern void complete_all(struct completion *);

這兩個函數在是否有多個線程在等待相同的完成事件上有所不同,complete只會喚醒一個等待線程,而complete_all允許喚醒所有等待線程;大多數情況下,只會有一個等待者,因此這兩個函數產生相同的結果;一個完成量通常是一個單次設備,也就是說,它只會被使用一次后就被丟棄;但是,完成量結構也可以重復使用,如果沒有使用complete_all,則我們可以重復使用一個完成量結構,只要那個將要觸發的事件是明確的,就不會有問題;但是如果使用了complete_all,則必須在重新使用該結構之前重新對它進行初始化;下面函數用來快速進行重新初始化;

1 static inline void reinit_completion(struct completion *x)

完成量的典型使用是在模塊退出時的內核線程終止;在這種原型中,某些驅動程序的內部工作由一個內核線程在while (1)循環中完成,當內核准備清除該模塊時,exit函數會告訴該線程退出並等待完成量;為了實現這個目的,內核包含了可用於這種線程的一個特殊函數;

1 void complete_and_exit(struct completion *comp, long code)

比如內核中下面代碼就說明這種場景:

 1 static int ldlm_pools_thread_main(void *arg)
 2 {
 3     struct ptlrpc_thread *thread = (struct ptlrpc_thread *)arg;
 4     int c_time;
 5 
 6     thread_set_flags(thread, SVC_RUNNING);
 7     wake_up(&thread->t_ctl_waitq);
 8 
 9     CDEBUG(D_DLMTRACE, "%s: pool thread starting, process %d\n",
10            "ldlm_poold", current_pid());
11 
12     while (1) {
13         struct l_wait_info lwi;
14 
15         /*
16          * Recal all pools on this tick.
17          */
18         c_time = ldlm_pools_recalc(LDLM_NAMESPACE_CLIENT);
19 
20         /*
21          * Wait until the next check time, or until we're
22          * stopped.
23          */
24         lwi = LWI_TIMEOUT(cfs_time_seconds(c_time),
25                   NULL, NULL);
26         l_wait_event(thread->t_ctl_waitq,
27                  thread_is_stopping(thread) ||
28                  thread_is_event(thread),
29                  &lwi);
30 
31         if (thread_test_and_clear_flags(thread, SVC_STOPPING))
32             break;
33         thread_test_and_clear_flags(thread, SVC_EVENT);
34     }
35 
36     thread_set_flags(thread, SVC_STOPPED);
37     wake_up(&thread->t_ctl_waitq);
38 
39     CDEBUG(D_DLMTRACE, "%s: pool thread exiting, process %d\n",
40            "ldlm_poold", current_pid());
41 
42     <strong>complete_and_exit(&ldlm_pools_comp, 0);</strong>
43 }

 

 1 static void ldlm_pools_thread_stop(void)
 2 {
 3     if (!ldlm_pools_thread)
 4         return;
 5 
 6     thread_set_flags(ldlm_pools_thread, SVC_STOPPING);
 7     wake_up(&ldlm_pools_thread->t_ctl_waitq);
 8 
 9     /*
10      * Make sure that pools thread is finished before freeing @thread.
11      * This fixes possible race and oops due to accessing freed memory
12      * in pools thread.
13      */
14     <strong>wait_for_completion(&ldlm_pools_comp);</strong>
15     kfree(ldlm_pools_thread);
16     ldlm_pools_thread = NULL;
17 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM