DeviceDriver(五):内核定时器

一:内核时间管理

1、硬件定时器提供时钟源,时钟源的频率可以设置,设置好以后就可以周期性的产生定时中断,系统就可以使用定时中断来计时。中断周期性的产生的频率就是系统频率也叫做节拍率,比如1000Hz,500Hz,100Hz就是系统节拍率。系统节拍率可以设置,在配置Linux内核时可以进行设置,默认值为100Hz也就是10ms,在Linux内核中用“HZ”表示:

# 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() */

2、Linux内核使用全局变量jiffies来记录系统从启动以来的系统节拍数,系统启动时会将jiffies初始化为0。

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

对于jiffies数据的溢出问题,Linux内核提供了一些API来进行处理:

#define time_after(a,b)		\
	(typecheck(unsigned long, a) && \
	 typecheck(unsigned long, b) && \
	 ((long)((b) - (a)) < 0))
#define time_before(a,b)	time_after(b,a)

#define time_after_eq(a,b)	\
	(typecheck(unsigned long, a) && \
	 typecheck(unsigned long, b) && \
	 ((long)((a) - (b)) >= 0))
#define time_before_eq(a,b)	time_after_eq(b,a)

time_after(a, b):一般a值为jiffies,b值为比较值。a超过b时返回真,否则为假。time_before函数返回值相反。函数time_after_eq与time_before_eq多了一个等于判断条件。

linux内核还提供几组jiffies与ms,us,ns之间换算的API:

unsigned int jiffies_to_msecs(const unsigned long j)
unsigned int jiffies_to_usecs(const unsigned long j)
inline u64 jiffies_to_nsecs(const unsigned long j)

unsigned long msecs_to_jiffies(const unsigned int m)
unsigned long usecs_to_jiffies(const unsigned int u)
unsigned long nsecs_to_jiffies(u64 n)

二:内核定时器

1、 Linux内核定时器使用方法只需要提供超时时间(类似于定时时间)和超时处理函数即可,当超时时间到达以后就会执行相应的超时处理函数,需要注意的是Linux内核定时器并不是周期性运行的,超时之后就会关闭,如果想要实现循环只需要在超时处理函数中重新启动定时器即可。

Linux内核定时器定义:

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;
};

2、内核定时器相关API

(1)初始化

void init_timer(struct timer_list *timer)

(2)注册定时器,想内核注册之后定时器就会开始运行

void add_timer(struct timer_list *timer)

(3)删除定时器,返回值为0表示定时器还没被激活,否则已经激活

int del_timer(struct timer_list * timer)

(4)等待定时器退出后(即使用完毕)再删除,不能用于中断上下文中

int del_timer_sync(struct timer_list *timer)

(5)修改定时值

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(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);
}

猜你喜欢

转载自blog.csdn.net/qq_34968572/article/details/103698005