Linux内核定时器

简介

我们在编译 Linux内核的时候可以通过图形化界面设置系统节拍率,按照如下路径打开配置界面: ../../_images/image-20210829151104816.png

可以看出,可选的系统节拍率为 100Hz、 200Hz、 250Hz、 300Hz、 500Hz和1000Hz,默认情况下选择 100Hz,编写 Linux驱动的时候会常常用到 HZ,HZ表示一秒的节拍数,也就是频率。即上面的这些频率值 定义在include/asm-generic/param.h里面

# undef HZ
# define HZ CONFIG_HZ    // 这里定义 CONFIG_HZ即是通过配置界面生成在.config里面的值
# define USER_HZ 100 
# define CLOCKS_PER_SEC (USER_HZ)

Linux内核使用全局变量 jiffies来记录系统从启动以来的系统节拍数,系统启动的时候会将 jiffies初始化为 0 ,jiffies定义在文件 include/linux/jiffies.h中,定义如下:

extern u64 __jiffy_data jiffies_64; 
extern unsigned long volatile __jiffy_data jiffies;

// jiffies_64 和jiffies一个是64位的一个是32位的,jiffies其实就是jiffies_64的低32位而已

常用api函数如下:

unkown 通常为jiffies,known 通常是需要对比的值。

函数 功能
time_after(unkown, known) unkown>known 超时 返回真,否则返回假
time_after_eq(unkown, known) unkown>=known 刚超时 返回真,否则返回假
time_before(unkown, known) unkown<known 未超时 返回真,否则返回假
time_before_eq(unkown, known) unkown<=known 刚未超时 返回真,否则返回假

用法示例

unsigned long timeout;
timeout = jiffies + (2 * HZ); /* 超时的时间点 */

/*************************************
具体的代码
************************************/

/* 判断有没有超时 */
if(time_before(jiffies, timeout)) {
 /* 超时未发生 */
} else {
 /* 超时发生 */
}

ms us ns 和jiffies之间的相互转换函数API

函数 功能
int jiffies_to_msecs(const unsigned long j) 将jiffies类型的参数 转换为 毫秒
int jiffies_to_usecs(const unsigned long j) 将jiffies类型的参数 转换为 微秒
u64 jiffies_to_nsecs(const unsigned long j) 将jiffies类型的参数 转换为 纳秒
long msecs_to_jiffies(const unsigned int m) 将毫秒转换为jiffies类型
long usecs_to_jiffies(const unsigned int u) 将微秒转换为jiffies类型
unsigned long nsecs_to_jiffies(u64 n) 将纳秒转换为jiffies类型

内核定时器

Linux内核使用 timer_list结构体表示内核定时器,基于内核系统时钟层面上的软件定时器

struct timer_list { 
	struct list_head entry; 
	unsigned long expires;            /* 定时器超时时间,单位是节拍数 */ 
	struct tvec_base *base; 
	void (*function)(unsigned long); /* 定时处理函数 */
	unsigned long data;              /* 要传递给function函数的参数 */ 
	int slack; 
};

内核定时器相关的系统的API函数

函数 功能
void init_timer(struct timer_list *timer) 初始化定时器。
void add_timer(struct timer_list *timer) 向 Linux内核注册定时器
int del_timer(struct timer_list * timer) 删除定时器,不管定时器有没有被激活,都可以使用此函数删除。返回值0:定时器还没被激活 1:定时器已经激活。
int mod_timer(struct timer_list *timer, unsigned long expires) 函数用于修改定时值,如果定时器还没有激活的话,该函数会激活定时

使用示例

struct timer_list timer;            /* 定义定时器 */ 
/* 定时器回调函数 */ 
void function(unsigned long arg) 
{ 
	/* 
	 * 定时器处理代码 
	 */ 
	 
	/* 如果需要定时器周期性运行的话就使用mod_timer 
	 * 函数重新设置超时值并且启动定时器。 
	 */ 
	 mod_timer(&dev->timertest, jiffies + msecs_to_jiffies(2000));  
}
/* 初始化函数 */
void init(void)
{
	init_timer(&timer); /* 初始化定时器 */
	timer.function = function; /* 设置定时处理函数 */
	timer.expires=jffies + msecs_to_jiffies(2000);/* 超时时间2秒 */
	timer.data = (unsigned long)&dev; /* 将设备结构体作为参数 */
	add_timer(&timer); /* 启动定时器 */
}	 
/* 退出函数 */
void exit(void)
{
	del_timer(&timer); /* 立即删除定时器 */
	/* 或者使用 */
	del_timer_sync(&timer); /* 会等定时器处理函数完成后在删除*/
}	 

内核常用的短延时函数

函数 功能
void ndelay(unsigned long nsecs) 纳秒 延时函数
void udelay(unsigned long usecs) 微秒 延时函数
void mdelay(unsigned long mseces) 毫秒 延时函数