延遲執行
驅動程式經常需要拖延某段特定程式碼的開始執行時間-通常是為了讓硬體有足夠餘裕完成某些工作。選擇延遲技術時候需要評估的重點是延遲時間與tick之間的關係,而且要將個平台的HZ考慮進去。倘若延遲時間超過一個tick,可以使用jiffies。如果是非常短延遲效果則必須借助軟體迴圈。
長期延遲
假如要延遲的時間超過好幾個tick,而且不是那麼在乎精確度,最容易的方法是寫一個持續觀察jiffies變化狀態的迴圈,我們稱為忙碌迴圈,但這對處理器的負擔會很大,常常有可能鎖死cpu的行程。
#include <linux/jiffies.h>
unsigned long j1;
j1 = jiffies+HZ; /*1秒之後的jiffies值*/
while(time_before(jiffies,j1))
cpu_relax();
渡讓處理器
忙碌迴圈會嚴重拖累高負載系統,因此我們應該尋求更好的技術,如果在等待期間將cpu讓出去,至於要讓給哪一個行程,就於排程器去處理。如果在一個非常忙錄系統上,呼叫排程器,反而也可能造成驅動程式等待了超過原本預期的時間,因為一但將cpu渡讓給其他行程,排程器就無法保證甚麼時候回將cpu還給你。如果你要延遲的時間長度是有所限制的,渡讓cpu也不是保險的做法。
while(time_before(jiffies,j1)){
schedule();
}
休眠延遲
如果驅動程式使用一個待命佇列來等待其他事件,但是你又希望一段時間之後要恢復運作,你可以選擇使用wait_event_timeout()
或wait_event_interruptible_timeout()
:
#include <linux/wait.h>
long wait_event_timeout(wait_queue_head_t q,conition,long timeout);
long wait_event_interruptible_timeout(wait_queue_head_t q,condition,long timeout);
兩者都會讓行程休眠於指定的待命佇列裡,但保證一定會在timeout之前返回。也就是它們可以控制行程的休眠期限,避免睡過頭。timeout是指要等待的jiffy個數,condition表示喚醒的條件,在休眠期間被喚醒函式會傳回剩餘未休眠的jiffy,如果剛好或是超過timeout的jiffy數則會傳回0,wait_event_timeout不可能傳回負值,wait_event_interruptible_timeout則可能傳回-ERESTARTSYS,表示休眠過程中曾發生的中斷事件。以上兩個函式都需要準備一個待命佇列,如果只要純粹的消磨時間,而不等待任何事件,可以使用schedule_timeout()函式:
#include <linux/sched.h>
signed long schedule_timeout(signed long timeout);
使用schedule_timeout()之前,必須先設定當時行程的狀態:
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(delay);
我們先用set_current_state()向schedule表示行程已經準備好休眠,而且休眠過程中可接受中斷。不願接受中斷可以用TASK_UNINTERRUPTIBLE代替。呼叫schedule_timeout()後,若沒有信號打擾,則應該在經過delay個jiffies後,行程才會恢復為TASK_RUNNING state。
短期延遲
驅動程式常給硬體一些時間去完成它們的工作,這段延遲時間頂多只有幾十個微秒(us),然而計時器中斷的間隔時間大約是幾十格毫秒(ms),因此核心提供三種等級的延遲函式,分別是ndelay、udelay、mdelay,就是針對這種狀況設計:
#include <linux/delay.h>
void ndelay(unsigned long nsecs);
void udelay(unsigned long usecs);
void mdelay(unsigned long msecs);
上述函式基本上都是以軟體迴圈的方式來模擬延遲效果,它們依據loops_per_jiffy這個整數變數來判斷迴圈次數。不能要求函式延遲太長時間,雖然引數型態都是unsigned long,如果給的引數超過一定程度,在計算迴圈數會發生整數溢位,不僅會失敗,同時還會出現一段有關__bad_udelay懸置符號(unresolved symbol)的錯誤訊息。同時要注意,這些延遲函式都是以忙碌迴圈做出來的,再延遲期間中,系統不能夠跑其他工作。
<linux/delay.h>
另外還提供一組不使用忙碌迴圈的毫秒級(或更久)延遲函式:
void msleep(unsigned int millisecs);
unsigned long msleep_interruptible(unsigned int millisecs);
void ssleep(unsigned int seconds);
前兩個函式可以呼叫他們的行程休眠millisecs所指定的毫秒數,但msleep()不容許休眠期間被中斷。msleep_interruptible()的回傳值通常是0,但如果行程被提早喚醒,回傳值是還沒休眠完畢的剩餘毫秒數。呼叫ssleep()會使行程進入不可中斷的休眠狀態,睡足指定秒數。