一段經典摘抄,OS的心跳,很重要,但很少引起人的注意,除非碰到相關問題。
1、節拍率——HZ:在alpha體繫結構上是1024,而在其它平台上,都為10數量級倍。在嵌入式ARM上為100(2.6核心)。這個值的意義是什麼呢,也就是在arm平台上時鐘中斷100次,為一秒。一般的情況下編程者不要改變這個值,因為核心編很多代碼都是有時間要求的,而且核心編寫都在很多地方都做了相應的最佳化與折衷處理,改變HZ的值會對系統的效能有很大的影響。
2、jiffies:這個值是用來記錄系統自系統啟動以來產生的節拍的總數,啟動時,核心將這個變數初始化為0;在每次的時鐘中斷處理常式都會增加該變數的值,jiffies一秒內增加的值就是HZ,系統已耗用時間以秒為單位計算,則為系統運行了jiffies/HZ秒。
在如下定義(2.6核心):
extern u64 __jiffy_data jiffies_64;
extern unsigned long volatile __jiffy_data jiffies;
在2.6的核心中它的變數類型從無符號長整型變為了u64,也就是說,即使是32位的機器,也使用無符號的64位整型表示jiffies。大多數的代碼只涉及jiffies的低32位,訪問jiffies的代碼中會讀到jiffies_64的低32位,可以通過get_ jiffies_64()函數讀取整個64位。在64位的體繫結構中jiffies_64和jiffies指的同一個變數,代碼可以既可以通過jiffies也可以通過get_ jiffies_64()讀取。
3、為了避免jiffies值的迴繞(溢出),核心中提供了幾個API函數來比較節拍計數。定義在<linux/jiffies.h>中。
/* time_is_before_jiffies(a) return true if a is before jiffies */
#define time_is_before_jiffies(a) time_after(jiffies, a)
/* time_is_after_jiffies(a) return true if a is after jiffies */
#define time_is_after_jiffies(a) time_before(jiffies, a)
/* time_is_before_eq_jiffies(a) return true if a is before or equal to jiffies*/
#define time_is_before_eq_jiffies(a) time_after_eq(jiffies, a)
/* time_is_after_eq_jiffies(a) return true if a is after or equal to jiffies*/
#define time_is_after_eq_jiffies(a) time_before_eq(jiffies, a)
例:
可以利用jiffies設定逾時等,譬如:
unsigned long timeout = jiffies + HZ * 2; // 2秒鐘後逾時
if(time_before(jiffies, timeout){
// 還沒有逾時
}
else{
// 已經逾時
}
4、時間類型
Linux下常用的時間類型有4個:time_t,struct timeval,struct timespec,struct tm。一般用於實際時間(牆上時間),定義在檔案中。
(1)time_t是一個長整型,一般用來表示用1970年以來的秒數,格林威治時間。
(2)Struct timeval有兩個成員,一個是秒,一個是微妙。
struct timeval{
long tv_sec; /**//* seconds */
long tv_usec; /**//* microseconds */
};
(3)struct timespec有兩個成員,一個是秒,一個是納秒。
struct timespec{
time_t tv_sec; /**//* seconds */格林威治時間
long tv_nsec; /**//* nanoseconds */
};
(4)struct tm是直觀意義上的時間表示方法:
struct tm {
int tm_sec; /**//* seconds */
int tm_min; /**//* minutes */
int tm_hour; /**//* hours */
int tm_mday; /**//* day of the month */
int tm_mon; /**//* month */
int tm_year; /**//* year */
int tm_yday; /**//* day in the year */
int tm_wday; /**//* day of the week */
int tm_isdst; /**//* daylight saving time */
};
:一般使用int gettimeofday(struct timeval *tv, struct timezone *tz);得到牆上時間,牆上時間一般會在使用者空間裡使用,而在核心空間裡,大多數情況下只要擷取相對時間就OK了,也就是說用jiffies搞定。
5、核心定時器
核心定時器可以理解為一個軟體定時器,它可以被動態建立、更改和銷毀等,而且運行次數沒有限制。
定時器同結構time_list表示,此結構體定義在<linux/timer.h>中。
struct timer_list {
struct list_head entry; //包含定時器的鏈表
unsigned long expires; //以jiffies為單位的定時值
void (*function)(unsigned long); //定時時間到的處理函數
unsigned long data; //傳給片處理函數的參數,可以用在共用一個處理函數的情況。
struct tvec_base *base; //內部值,使用者呼略是安全的
#ifdef CONFIG_TIMER_STATS
void *start_site;
char start_comm[16];
int start_pid;
#endif
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
在使用定時器時,沒有必要去深入瞭解這個資料結構體的成員。可以使用在中的API來操作。
增加定時器
void add_timer(struct timer_list * timer);
刪除定時器
int del_timer(struct timer_list * timer);
修改定時器的expire
int mod_timer(struct timer_list *timer, unsigned long expires);
使用定時器的一般流程為:
(1)定義timer、編寫function;
(2)為timer的expires、data、function賦值;
(3)調用add_timer將timer加入列表;
(4)在定時器到期時,function被執行;
(5)在程式中涉及timer控制的地方適當地調用del_timer、mod_timer刪除timer或修改timer的expires。
補充:一般應該使用del_timer_sync()來代替del_timer(),在俺的板子上就同沒有必要了,因為俺的板子是單一處理器的,在SMP中一定要代替使用。
6、短延遲
前面所講到的時間都是很長的了啦,在核心中提供了二個更短延遲的函數來供開發人員使用
udelay(unsigned long usecs); //微秒
mdelay(unsigned long msecs); //毫秒
前者用軟體迴圈指定的微妙數,後者調用前者達到延遲毫秒級。udelay 函數只能用於擷取較短的時間延遲,因為loops_per_second值的精度只有8位,所以,當計算更長的延遲時會積累出相當大的誤差。儘管最大能允 許的延遲將近1秒(因為更長的延遲就要溢出),推薦的 udelay 函數的參數的最大值是取1000微秒(1毫秒)。延遲大於 11 毫秒時可以使用函數 mdelay。
要特別注意的是 udelay 是個忙等待函數(所以 mdelay 也是),在延遲的時間段內無法運行其他的任務,因此要十分小心,尤其是 mdelay,除非別無他法,要盡量避免使用。