轉自:http://www.xuebuyuan.com/510594.html
5-3 Linux內核計時、延時函數與內核定時器
計時
1、
內核時鍾
1.1
內核通過定時器(timer)中斷來跟蹤時間流
1.2
硬件定時器以周期性的間隔產生時間中斷,這個間隔(即頻率)由內核根據HZ來確定,HZ是一個與體系結構無關的常數。
1.3
這個時間間隔通常取1ms到10ms.
2、
jiffies計算器
2.1每次當定時器中斷發生時,內核內部通過一個64位的變量jiffies_64做加一計數。
2.2驅動程序開發者通常訪問的是jiffies變量,它是jiffes_64的低32位。
2.3jiffies是unsigned
long型的變量,該變量被聲明為volatile,這樣可避免編譯器對訪問該變量的語句的優化。
2.4jiffies記錄了自最近一次Linux啟動后到當前的時間間隔(即時鍾中斷發生的次數)。驅動程序常利用jiffies來計算不同事件間的時間間隔。
3、HZ
3.1 HZ表每秒中產生的定時中斷次數
3.2 HZ就代表1秒,HZ/2就代表0.5秒
4、使用jiffies計數器
#include<linux/jiffies.h>
Unsigned long current_i, stamp_1,
stamp_half, stamp_n;
Current_i = jiffies; //讀取當前的值
Stamp_1 = current_i+HZ; //未來的一秒
Stamp_half = current_i +HZ/2 ; //未來的半秒
Stamp_n = current_i + n*HZ/1000; //未來的n毫秒
5、為了防止jiffies溢出導致問題,最好使用宏比較
#include<linux/jiffies.h>
Int time_after(unsigned long a, unsigned long b);//判斷a代表的時間是否在b之后。
Int time_before(unsigned long a,unsigned long b);
Int time_after_eq(unsigned long a ,unsigned long b);
Int time_before_eq(unsigned long a,unsigned long b);
6、
(1)用戶空間的timeval
struct timeval{
time_t tv_sev;//秒、
suseconds_t tv_usec;//毫秒
}
(2)用戶空間的timespec
struct timespec{
time_t tv_sec;//秒
long tv_nsec;//納秒
}
7、內核空間jiffies和用戶空間的timeval、timevspec的轉換。
#include<linux/time.h>
unsigned long timespec_to_jiffies(struct timespec* value);
void jiffies_to_timespec(unsigned long jiffie,struct timespec* value);
unsigned long timeval_to_jiffies(struct timeval* value);
void jiffies_to_timeval(unsigned long jiffies, struct
timeval* value);
8、獲取當前時間
#include<linux/time.h>
void do_gettimeofday(struct timeval*
tv);
struct timespec current_kernel_time(void);
9、使用jiffies延時(如果對延時的精度要求不是很高時,用忙等待)
unsigned long j = jiffies + delay*HZ
while(jiffies<j){ //jiffies表當前的滴答值,是不停地走的。
/*do nothing*/
}
10、長延遲。
while(time_before(jiffies,end_time)){
Schedule();//在end_time之前,則調度
}
#include<linux/sched.h>
signed long schedule_timeout(signed long timeout);//使用jiffies表示的延遲。先做超時,
典型應用:
set_current_state(TASK_INTERRUPTIBLE);//設置狀態值,設置為可中斷的睡眠
schedule_timeout(delay);//延時
11、短延時(忙等待延時,不發生休眠)
#include<linux/delay>
//以下三個延時函數均是忙等待函數,因而在延遲過程中午飯運行其他任務。不發生休眠的。
void ndelay(unsigned long nsecs); //延時納秒
void udelay(unsigned long usecs);//延時微秒
void mdelay(unsigned long msecs);//延時毫秒
12、不用忙等待的延時方式(將調用進程休眠給定時間)
#include<linux/delay.h>
void msleep(unsigened int millisecs);//休眠millisecs毫秒
不可中斷的休眠millisecs毫秒
unsigned long msleep_interruptible(unsigned int millisecs);//可中斷的休眠
void ssleep(unsigned int seconds);//休眠seconds秒。
內核定時器:
13、定時器用於控制某個函數(定時器處理函數)在未來的某個特定時間執行。內核定時器注冊的處理函數只執行一次—不是循環執行的。
14、內核定時器被組織成雙向鏈表,並使用struct
time_list 結構描述。
#include<linux/timer.h>
struct timer_list{
unsigned long expires; //超時的jiffies
void (*function)(unsigned long);//超時處理函數
unsigned long data; //超時處理函數參數。
};
15、內核定時器操作函數
15.1初始化(初始化定時器隊列結構)
void init_timer(struct timer_list * timer);
struct timer_list TIMER_INITIALIZER(_function,_expires,_data);
15.2添加定時器(啟動定時器,開始倒計時)
void add_timer(struct time_list *tiemer0:
15.3刪除定時器(在定時器超時前將它刪除。定時器超時后,系統會自動地將它刪除)
15.4內核定時器使用模板
static struct timer_list key_timer;//定義內核定時器對象
static void key_timer_handle(unsigned ong data)//定時器處理函數
{
……
//定時器處理函數具體執行代碼
……
// 定時器參數的更新,重啟定時器
key_timer.expires = jiffies+ KEY_TIMER_DELAY;
add_time(&key_time);
//賦新值,可以實現
循環
……
}
//設備驅動模塊加載函數
static int__ init xxx_init(void)
{
……
//初始化內核定時器
init_time(&key_timer);
key_timer.function=&key_timer_handle;
key_timer.data = (unsigned long)key_desc;
key_timer.expires = jiffies+KEY_TIMER_DELAY;
//添加內核定時器(這是第一次啟動)
add_time(&key_time);
……
}
Static void__exit xxx_exit(void)
{
……
//刪除定時器
del_timer(&key_timer);
……
}
16、內核定時器與tasklet比較
16.1相同點:
在中斷期間運行,時鍾會在調度他們的同一個cpu上運行,軟件中斷的上下文,原子模式運行。(軟件中斷時打開硬件中斷的同時執行某些異步任務的一種內核機制)
16.2不同的:不能要求TASKLECT在給定的時間執行。
聲明:本文非原創,整理自申嵌