xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
這節將介紹一些很枯燥的內核,大體是內核中時間的概念和內核延時的使用,並沒有源代碼。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
一、內核時間的相關概念
牆上時鐘:也就是實際時間。
系統時間:自系統啓動開始所經過的時間。
時鐘中斷:內核會週期性的產生時鐘中斷,在中斷處理函數中執行一些與時間相關的操作,如更新時間,進程調度,檢查時間片等。
節拍率:在linux內核中,通過編程定義節拍率,也就是HZ。每1/HZ秒發生一次時鐘中斷。在ARM中,節拍率被定義爲100,節拍率越大,系統進入時鐘中斷就越頻繁,時間和進程調度等操作就越準確,但對系統的負擔也就越大。
jiffies:該32位(unsigned long)的全局變量用來記錄自系統啓動以來產生的節拍的總數。系統啓動時清零,每次時鐘中斷加一。所以,一秒內的時鐘中斷次數(或者說jiffies一秒內增加的值)也就等於HZ。如果系統時間以秒來表示,那就等於jiffies/HZ秒。
實時時鐘(RTC):體系結構中用於維持系統時間的設備,就像電腦的BIOS,需要在關機狀態時通過電池供電。系統啓動時通過讀取RTC來初始化牆上時鐘。
在這裏要補充說一下HZ的值,來自Tekkaman Ninja的博客。
http://blog.chinaunix.net/space.php?uid=20543672&do=blog&id=94309
在ARM下可以看到如下定義:
/*arch/arm/include/asm/param.h*/
13 #ifdef __KERNEL__
14 # define HZ CONFIG_HZ /* Internal kernel timer frequency */
15 # define USER_HZ 100 /* User interfaces are in "ticks" */
16 # define CLOCKS_PER_SEC (USER_HZ) /* like times() */
17 #else
18 # define HZ 100
19 #endif
可以看到:
1、用戶空間但到的HZ是100。
2、內核空間的HZ有CONFIG_HZ和__KERNEL__決定,而CONFIG_HZ在.config中定義。
在看另外一處,查看自己編譯內核時使用的.config文件:
/*linux-2.6.29/.config */
275 CONFIG_HZ=200
所以,我的當前開發板的HZ定義爲200,不信的話,可以自己 打印來看看。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
二、內核延時
內核提供了很多延時的方法,接下來一一介紹。
2.1、忙等待:
這是最簡單的延時方法,直接來段代碼說明:
unsigned long delay = jiffies + 5*HZ //5*HZ = 5秒
while(delay < jiffies)
;
內核也有另外一種版本:
unsigned long delay = jiffies + 5*HZ //5*HZ = 5秒
while(delay < jiffies)
cpu_relax; //但ARM下cpu_relax是空語句。
這代碼很簡單,每時每刻查詢當前時間(jiffies)是否已經超出延時(delay)。在還沒到達延時的情況下,處理器只能原地旋轉等待,一直耗費CPU資源。
所以有了改進版:讓出處理器。
unsigned long delay = jiffies + 5*HZ //5*HZ = 5秒
while(delay < jiffies)
schedule(); //讓出處理器
雖然當前進程讓出處理器,但是它仍在運行隊列中,如果系統中只有它這個可運行進程,那麼該進程又會重新被執行。那就是說,在延時這段時間內,內核重複一個操作,調度進程。所以我把這個也理解成忙等待。
說一些題外話,jiffies是一個unsigned long類型的全局變量,當加到4294967295時溢出,從零開始繼續增加,這也叫回繞。
由於迴繞的問題,內核提供了四個宏來比較超時,它們能正確的處理節拍數迴繞情況。
/*linux/jiffies.h */
106 #define time_after(a,b) \
107 (typecheck(unsigned long, a) && \
108 typecheck(unsigned long, b) && \
109 ((long)(b) - (long)(a) < 0))
110 #define time_before(a,b) time_after(b,a)
111
112 #define time_after_eq(a,b) \
113 (typecheck(unsigned long, a) && \
114 typecheck(unsigned long, b) && \
115 ((long)(a) - (long)(b) >= 0))
116 #define time_before_eq(a,b) time_after_eq(b,a)
至於爲什麼能防止迴繞,我也糾結了一個上午,該博客有詳細的講解,順便感嘆一下牛人無處不在:
http://blog.csdn.net/yuanlulu/archive/2010/11/18/6019862.aspx
上面的代碼修改一下:
unsigned long delay = jiffies + 5*HZ //5*HZ = 5秒
while(time_before(jiffies, delay))
schedule(); //讓出處理器
2.2、短延時:
忙等待的最小時間間隔是1/HZ秒,假設HZ的值爲100,那忙等待的時間間隔最小也只是10ms。但在有些內核代碼中,不但需要很短的延時,而且時間精確度較高。
#include
<linux/delay.h>
void ndelay(unsigned long nsecs);
void udelay(unsigned long usecs);
void mdelay(unsigned long msecs);
udelay(150); //延時150微秒
說明一下:
1、一般的體系架構都沒辦法達到納秒級的延時標準。
2、udelay的實現是靠執行次數循環達到延時效果。內核知道處理器一秒能執行多少次循環,udelay根據執行的延時時間在1s中的比例,得出需要延時的次數來達到延時。
3、mdelay是基於udelay實現的。超過1ms的延時不要使用udelay,建議使用mdelay。
4、不管是哪種延時,真正的延時至少會達到要求的延時時間,但可能更長。
5、它們也屬於忙等待的一種,不過延時時間較短。
2.3、schedule_timeout():
更理想的延時方法是使用schedule_timeout()函數,該方法讓需要延時的任務睡眠,直到指定延時時間耗盡後重新執行。當然它也不能保證睡眠時間和延時時間一致,只能儘量接近。
用法如下:
/*將任務設置爲可中斷睡眠狀態,當然你也可以設置爲TASK_UNINTERRUPTIBLE,但不建議*/
set_current_state(TASK_INTERRUPTIBLE);
/*小睡一會,s秒後喚醒*/
schedule_timeout(s*HZ);
2.4、設置超時時間,在等待隊列中睡眠:
在字符設備驅動的時候已經介紹過等待隊列的基本原理:當任務放在等待隊列中,然後調度其他進程執行,一旦等待的事情成立,調用wake_up喚醒等待隊列中的進程並重新投入運行。
上面講得是函數wait_event_interruptible。在這個函數的基礎上,增加了延時功能。如果在特定延時時間內等待事件到來,那任務被喚醒。否則,等到特定延時時間耗盡後事件還沒發生,那也得喚醒任務。
/*linux/wait.h*/
wait_event_interruptible_timeout(wq, condition, timeout)
使用大概如下:
wait_queue_head_t wait;
init_qaitqueue_head(&wait);
wait_event_interruptible_timeout(wait, 0, s*HZ); //延時s秒
上面的調用condition爲0,那表示等待時間永遠不成立,只有時間到才能喚醒,相當於:
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(s*HZ);
而且,schedule_timeout還少了等待隊列創建的操作,減少內核負擔。所以,如果不是既要等待延時,又要等待時間發生,那就沒必要用到等待隊列的延時了。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
三、總結
這一節,簡單介紹了內核時間的概念,並知道了幾種常用的延時方法。
最後提醒一下,在持有鎖和禁止中斷時使用忙等待,因爲這樣會降低系統的速度和性能。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
源代碼:無
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx