linux内核中有大量的函数需要时间管理,硬件提供时钟源,时钟源的频率可以设置,设置好以后产生周期性中断,系统通过中断计时。
1.节拍率(tick rate)
系统周期中断频率,可通过menuconfig设置,可设置 100、200、250、300、500、1000hz。
-> Kernel Features
-> Timer frequency (<choice> [=y])
设置后,在 ./config 文件内生成 CONFIG_HZ = xxx
动态系统时钟参数,驱动代码可使用宏定义 HZ 定时使用。
/include/asm-generic/param.c
# undef HZ
# define HZ CONFIG_HZ /* Internal kernel timer frequency */
# define USER_HZ 100 /* some user interfaces are */
# define CLOCKS_PER_SEC (USER_HZ) /* in "ticks" like times() */
#endif /* __ASM_GENERIC_PARAM_H */
2.jiffies
记录系统节拍率
/include/linux/jiffies.h
extern u64 __jiffy_data jiffies_64;
extern unsigned long volatile __jiffy_data jiffies;
jiffies_64用于64位系统,jiffies用于32位系统。jiffies结构图,图中jiffies_64和jiffies本质一样。
系统连续运行,jiffies存在溢出上限,linux定义了几个函数处理jiffies的绕回。
/include/linux/jiffies.h
#define time_after(a,b) //returns true if the time a is after time b.
#define time_before(a,b) time_after(b,a)
#define time_after_eq(a,b)
#define time_before_eq(a,b) time_after_eq(b,a)
a通常为jiffies, b为对比值。
代码实现:
unsigned long timeout;
timeout = jiffies + ( 3 * HZ); /* 延时3s*/
/*判断函数*/
if(time_after(jiffies, timeout)){
/*超时发生*/
}else{
/*超时没发生*/
};
为了方便开发,linux定义了一些jiffies 与时间的转换函数:
extern unsigned int jiffies_to_msecs(const unsigned long j);
extern unsigned int jiffies_to_usecs(const unsigned long j);
static inline u64 jiffies_to_nsecs(const unsigned long j)
extern unsigned long msecs_to_jiffies(const unsigned int m);
extern unsigned long usecs_to_jiffies(const unsigned int u);
extern unsigned long timespec_to_jiffies(const struct timespec *value);
便于阅读,上述代码可改为:
unsigned long timeout;
timeout = jiffies + msecs_to_jiffies(3000);
/*判断函数*/
if(time_after(jiffies, timeout)){ /* 延时3s*/
/*超时发生*/
}else{
/*超时没发生*/
};
3.内核定时器
定时器结构体
/include/linux/timer.h
struct timer_list {
/*
* All fields that change during normal runtime grouped to the
* same cacheline
*/
struct list_head entry;
unsigned long expires; /*延时时间(节拍数)*/
struct tvec_base *base;
void (*function)(unsigned long); /*定时器函数*/
unsigned long data; /*定时器传递参数*/
int slack;
#ifdef CONFIG_TIMER_STATS
int start_pid;
void *start_site;
char start_comm[16];
#endif
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
定时器使用流程:
- 定义定时器结构体。
- 初始化结构体。
void init_timer(struct timer_list *timer)
- 注册定时器到内核。
void add_timer(struct timer_list *timer)
- 由于定时器使用一次后自动关闭,重启定时器。
int mod_timer(struct timer_list *timer, unsigned long expires)
- 关闭定时器。
int del_timer_sync(struct timer_list *timer)
代码流程:
struct timer_list timer; /* 定义定时器 */
/* 定时器回调函数 */
void function(unsigned long arg)
{
/*
* 定时器处理代码
*/
/* 如果需要定时器周期性运行的话就使用 mod_timer
* 函数重新设置超时值并且启动定时器。
*/
mod_timer(&dev->timertest, jiffies + msecs_to_jiffies(2));
}
/* 初始化函数 */
void init(void)
{
init_timer(&timer); /* 初始化定时器 */
timer.function = function; /* 设置定时处理函数 */
timer.expires=jffies + msecs_to_jiffies(2);/* 超时时间 2 秒 */
timer.data = (unsigned long)&dev; /* 将设备结构体作为参数 */
add_timer(&timer); /* 启动定时器 */
}
/* 退出函数 */
void exit(void)
{
del_timer(&timer); /* 删除定时器 */
/* 或者使用 */
del_timer_sync(&timer);
}
4. DEMO
驱动:
https://github.com/Jiongyu/linux_driver_example/blob/master/12_timer/timer.c
测试
https://github.com/Jiongyu/linux_driver_example/blob/master/12_timer/timerApp.c