長延遲
有些驅動程序需要延遲比較長的時間,即長於一個時鍾滴答;
忙等待
如果想把執行延遲若干個時鍾滴答,或者對延遲的精度要求不高,最簡單的實現方法就是一個監視jiffies計數器的循環;這種忙等待的實現方法通常具有下面形式,其中j1是延遲終止的jiffies值:
1 while (time_before(jiffies, j1)) 2 cpu_relax();
對cpu_relax的調用將以架構相關的方式執行,其中不執行大量的處理器代碼;在許多系統上,該函數根本不會做任何事情;而在SMP系統上,它可能將處理器讓給其他線程;但是,只要可能,我們應該避免使用這種方式,這里提到它,只是因為可能偶爾需要運行這段代碼,以便更好的理解其他延遲計數;
讓出處理器
忙等待為系統整體增加了沉重的負擔,因此有必要尋找更好的延遲計數,比如在不需要CPU時主動釋放CPU,這可以通過調用shcedule函數實現;
1 while (time_before(jiffies, j1)) { 2 schedule(); 3 }
當進程使用schedule釋放處理器之后,沒有任何保證說進程可以再隨后很快就能得到處理器;除了影響計算機系統整體性能之外,這種用法對驅動程序的需求並不安全,因為延遲可能遠大於需求;
超時
通過監視jiffies計數器實現的延遲循環可以工作,但不是非常理想;存在兩種構造基於jiffies超時的途徑,使用哪個規則則依賴於驅動程序是否在等待其他事件;
如果驅動程序使用等待隊列類等待其他一些事件,而我們同時希望在特定的時間段內運行,則可以使用wait_event_timeout或者wait_event_interruptible_timeout函數:
1 #define wait_event_timeout(wq, condition, timeout) 2 #define wait_event_interruptible_timeout(wq, condition, timeout)
上述函數會在給定的等待隊列上休眠,但是會在超時到期時返回;這樣,這兩個函數實現了一種有界的休眠,這種休眠不會永遠繼續;注意,這里的timeout標識要等待的jiffies值,而不是絕對時間值;如果超時到期,這個兩個函數會返回零;如果進程由其他事件喚醒,則會返回剩余的延遲時間,並用jiffies表達;返回值不會是負數,及時因為系統負荷而導致真正的延遲時間超過預期;
上述兩個函數需要有人在等待隊列上調用wake_up函數,或者超時到期;而在沒有人會在等待隊列上調用wake_up的情況下,進程將始終會在超時到期時被喚醒;為了適應這種不等待任何事件而延遲的情況,內核提供了schedule_timeout函數;
1 signed long __sched schedule_timeout(signed long timeout)
這里,timeout是用jiffies表示的延遲時間,正常返回值是0,除非在給定超時值之前函數返回(比如響應某個信號);schedule_timeout要求調用者首先設置當前進程的狀態,典型的調用如下:
1 set_current_state(TASK_INTERRUPTIBLE); 2 schedule_timeout(delay);
需要注意的是超時到期和真正唄調度執行之間,需要額外的時間;
短延遲
當設備驅動程序需要處理硬件的延遲時,這種延遲通常最多涉及到幾十個毫秒;這種情況下,依賴於時鍾滴答顯然不是正確的方法;
ndelay,udelay和mdelay這幾個內核函數很好的完成短延遲任務,它們分別延遲指定數量的納秒、微秒和毫秒時間,它們的原型如下:
1 #define mdelay(n) 2 #define udelay(n) 3 #define ndelay(n)
需要注意的是,這三個延遲函數均是忙等待函數,因而在延遲過程中無法運行其他任務;因為,我們只能在沒有其他實用方法時使用這些函數;
實現毫秒級(或者更長)延遲還有另一種方法,這種方法不涉及忙等待;
1 void msleep(unsigned int msecs) 2 unsigned long msleep_interruptible(unsigned int msecs) 3 void ssleep(unsigned int seconds)
通常,如果我們能夠容忍比所請求更長的延遲,則應當使用schedule_timeout、sleep或者ssleep;