linux設備驅動歸納總結(七):1.時間管理與內核延時

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、用戶空間但到的HZ100

2、內核空間的HZCONFIG_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; //ARMcpu_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、一般的體系架構都沒辦法達到納秒級的延時標準。

2udelay的實現是靠執行次數循環達到延時效果。內核知道處理器一秒能執行多少次循環,udelay根據執行的延時時間在1s中的比例,得出需要延時的次數來達到延時。

3mdelay是基於udelay實現的。超過1ms的延時不要使用udelay,建議使用mdelay

4、不管是哪種延時,真正的延時至少會達到要求的延時時間,但可能更長。

5、它們也屬於忙等待的一種,不過延時時間較短。


2.3schedule_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

上面的調用condition0,那表示等待時間永遠不成立,只有時間到才能喚醒,相當於:

set_current_state(TASK_INTERRUPTIBLE);

schedule_timeout(s*HZ);

而且,schedule_timeout還少了等待隊列創建的操作,減少內核負擔。所以,如果不是既要等待延時,又要等待時間發生,那就沒必要用到等待隊列的延時了。


xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


三、總結


這一節,簡單介紹了內核時間的概念,並知道了幾種常用的延時方法。

最後提醒一下,在持有鎖和禁止中斷時使用忙等待,因爲這樣會降低系統的速度和性能。

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

源代碼:無

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章