文章目錄
- 一、jiffies定時器
- 1.1 工作原理
- 1.2 timer_list結構體
- 1.3 相關接口
- 1.3.1 初始化和啟動定時器
- 1.3.2 修改定時器
- 1.3.3 刪除定時器
- 1.3.4 jiffies相關接口
- 二、高精度定時器
- 2.1 hrtimer結構體
- 2.2 相關接口
- 2.2.1 初始化和啟動定時器
- 2.2.2 取消定時器
- 2.2.3 通過定時器實現周期性任務
一、jiffies定時器
傳統定時器(基于jiffies)是Linux內核中最早和最基本的定時機制。jiffies是一個全局變量,它記錄了自系統啟動以來的“滴答數”(ticks)。每次時鐘中斷發生時,jiffies會增加。這個機制基于系統時鐘中斷的頻率,因此其精度受到時鐘頻率的限制。
1.1 工作原理
- 時鐘中斷:
時鐘中斷是一個固定頻率的周期性中斷事件,每次時鐘中斷發生時,內核會增加jiffies的值;
時鐘頻率通常由宏HZ定義,比如HZ=1000表示每秒發生1000次時鐘中斷(每毫秒一次); - jiffies變量:
jiffies是一個全局變量,用于記錄自系統啟動以來的時鐘中斷次數;
在每次時鐘中斷處理過程中,內核會增加jiffies的值;
1.2 timer_list結構體
傳統定時器使用struct timer_list結構體來定義和管理定時器任務。
struct timer_list {struct hlist_node entry;unsigned long expires; // 定時器到期的jiffies值void (*function)(unsigned long); // 定時器回調函數unsigned long data; // 回調函數參數
};
1.3 相關接口
定時器相關接口定義在kernel\time\timer.c
文件中。主要流程涉及以下接口。
1.3.1 初始化和啟動定時器
使用add_timer
接口啟動定時器,參數是timer_list
:
void add_timer(struct timer_list *timer)
代碼示例:
#include <linux/timer.h>
#include <linux/jiffies.h>static struct timer_list my_timer;void my_timer_callback(unsigned long data)
{printk(KERN_INFO "Timer callback function called with data: %lu\n", data);
}void setup_timer(void)
{// 初始化定時器init_timer(&my_timer);my_timer.function = my_timer_callback;my_timer.data = 0; // 回調函數參數my_timer.expires = jiffies + HZ; // 1秒后到期// 添加定時器到內核add_timer(&my_timer);
}
類似的接口還有add_timer_on
,該接口功能是在指定的cpu上啟動一個定時器:
void add_timer_on(struct timer_list *timer, int cpu)
1.3.2 修改定時器
使用mod_timer
接口修改定時器的超時時間:
void my_timer_callback(unsigned long data)
{printk(KERN_INFO "Timer callback function called with data: %lu\n", data);// 如果需要周期性定時器, 使用mod_timer修改定時器超時時間mod_timer(&my_timer, jiffies + msecs_to_jiffies(500));
}
在上述定時器回調接口my_timer_callback
中,調用mod_timer
修改定時器的超時時間為500ms,這樣能給實現每隔500ms循環調用定時器超時回調接口。
mod_timer
接口的功能相當于del_timer(timer); timer->expires = expires; add_timer(timer);
,即刪除定時器、設置定時器超時時間、啟動定時器。
1.3.3 刪除定時器
使用del_timer
接口刪除定時器:
del_timer(&my_timer);
1.3.4 jiffies相關接口
上述定時器單位是以jiffies為單位的,添加定時器和修改定時器的超時時間,都是以jiffies為單位的。下面介紹幾個常用的jiffies與其它時間單位轉化的接口:
static __always_inline unsigned long msecs_to_jiffies(const unsigned int m);
unsigned int jiffies_to_msecs(const unsigned long j);
static __always_inline unsigned long usecs_to_jiffies(const unsigned int u);
unsigned int jiffies_to_usecs(const unsigned long j);static inline unsigned long timespec_to_jiffies(const struct timespec *value);
static inline void jiffies_to_timespec(const unsigned long jiffies,struct timespec *value);unsigned long timeval_to_jiffies(const struct timeval *value);
void jiffies_to_timeval(const unsigned long jiffies, struct timeval *value);
二、高精度定時器
傳統定時器的精度和實時性受限于系統的時鐘中斷頻率和調度機制;
相對于傳統定時器(jiffies),高精度定時器可以提供納秒級的定時精度,適用于實時應用、精確計時等需求;
2.1 hrtimer結構體
struct hrtimer {struct timerqueue_node node; // 定時器隊列節點ktime_t _softexpires; // 定時器到期時間enum hrtimer_restart (*function)(struct hrtimer *); // 回調函數enum hrtimer_mode state; // 定時器狀態clockid_t clock_id; // 使用的時鐘類型
};
2.2 相關接口
2.2.1 初始化和啟動定時器
linux中,可以使用hrtimer_init
接口初始化一個高精度定時器;
使用hrtimer_start
接口啟動定時器;
示例代碼:
#include <linux/hrtimer.h>
#include <linux/ktime.h>
#include <linux/module.h>
#include <linux/init.h>static struct hrtimer my_hrtimer;enum hrtimer_restart my_timer_callback(struct hrtimer *timer)
{printk(KERN_INFO "High-Resolution Timer Callback\n");// 如果需要周期性定時器,重新啟動定時器hrtimer_forward_now(timer, ns_to_ktime(1000000000)); // 1秒return HRTIMER_RESTART;
}void setup_hrtimer(void)
{ktime_t ktime;// 初始化高精度定時器hrtimer_init(&my_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);my_hrtimer.function = &my_timer_callback;// 設置定時器到期時間ktime = ktime_set(0, 1000000000); // 1秒hrtimer_start(&my_hrtimer, ktime, HRTIMER_MODE_REL);
}
在示例代碼中,使用hrtimer_init
初始化一個定時器,使用hrtimer_start
接口設置定時器超時時間是 1s 并啟動定時器。
2.2.2 取消定時器
當模塊卸載或者不需要定時器時,使用hrtimer_cancel
接口取消定時器:
ret = hrtimer_cancel(&my_hrtimer);
2.2.3 通過定時器實現周期性任務
在定時器回調函數接口中,使用hrtimer_start
重啟啟動定時器,或者使用hrtimer_forward_now
接口更新定時器的到期時間,都能夠實現周期性任務。
hrtimer_forward_now:將定時器的到期時間向前推進一個時間間隔;適合保持固定周期,即使回調函數的執行時間可能有波動。
hrtimer_start:重新啟動定時器,可以靈活地設置新的到期時間;適用于需要在每次回調函數中動態調整定時器時間的場景。
代碼示例:
enum hrtimer_restart my_timer_callback(struct hrtimer *timer)
{printk(KERN_INFO "High-Resolution Timer Callback\n");// 如果需要周期性定時器,重新啟動定時器// 方法一:hrtimer_forward_now(timer, ns_to_ktime(1000000000)); // 1秒// 方法二:hrtimer_start(&my_hrtimer, ktime, HRTIMER_MODE_REL);return HRTIMER_RESTART;
}
注意:
- 雖然 hrtimer 提供納秒級的精度,但是實際精度受硬件和系統調度的影響,會導致定時器實際觸發的時間可能會有稍微的偏差;
- 這就導致使用
hrtimer_start
接口實現周期性任務時,每次產生一些偏差,可能會產生累計誤差; hrtimer_forward_now
接口是在定時器當前這次理論到期時間基礎上,向前推進一個周期設置下次超時時間,所以不會有累積誤差。如果對周期性的時間準確性有要求,建議使用hrtimer_forward_now
接口。