Linux内核定时器

佐手、 提交于 2020-03-01 11:51:55

Linux内核定时器

一、内核定时器介绍

  内核定时器是内核用来控制在未某个时间点(基于jiffies(节拍总数))调度执行某个函数的一种机制,相关函数位于<linux/timer.h> 和 kernel/timer.c 文件中。
  当内核定时器定时时间到达时,会进入用户指定的函数,相当于软中断。内核定时器注册开启后,运行一次就不会再运行(相当于自动注销),我们可以重新设置定时器的超时时间,让定时器重复运行。
  每当时钟中断发生时,全局变量jiffies(一个32位的unsigned long 变量)就加1,因此jiffies记录了linux系统启动后时钟中断发生的次数,驱动程序常利用jiffies来计算不同事件间的时间间隔。内核每秒钟将jiffies变量增加HZ次。因此,对于HZ值为100的系统,jiffy+1等于隔了10ms,而对于HZ为1000 的系统, jiffy+1仅为1ms。
  注意: jiffies 变量不能被修改,修改会出现错误。

二、内核定时器相关API

1、内核定时器结构体

struct timer_list {
	/*
	 * All fields that change during normal runtime grouped to the  same cacheline
	 */
	struct hlist_node	entry;
	unsigned long		expires;//设置超时时间,jiffies+xxx
	void			(*function)(unsigned long);//定时时间到后处理的函数
	unsigned long		data;//传递给上面函数的参数
	u32			flags;

#ifdef CONFIG_LOCKDEP
	struct lockdep_map	lockdep_map;
#endif
};

2、初始化定时器

//初始化定时器宏定义
#define init_timer(timer)//timer为定时器结构体指针						\
	__init_timer((timer), 0)
//初始化定时器具体实现
#define __init_timer(_timer, _flags)					\
	do {								\
		static struct lock_class_key __key;			\
		init_timer_key((_timer), (_flags), #_timer, &__key);	\
	} while (0)

3、初始化定时器并赋值

//初始化定时器并赋值宏定义
#define setup_timer(timer, fn, data)					\
	__setup_timer((timer), (fn), (data), 0)
//初始化定时器并赋值具体实现
#define __setup_timer(_timer, _fn, _data, _flags)			\
	do {								\
		__init_timer((_timer), (_flags));			\
		(_timer)->function = (_fn);	//定时到处理函数	\
		(_timer)->data = (_data);//传递给处理函数的参数	\
	} while (0)	

4、用宏来初始化定时器

//宏定义
#define TIMER_INITIALIZER(_function, _expires, _data)		\
	__TIMER_INITIALIZER((_function), (_expires), (_data), 0)
//实现部分
#define __TIMER_INITIALIZER(_function, _expires, _data, _flags) { \
		.entry = { .next = TIMER_ENTRY_STATIC },	\
		.function = (_function),			\
		.expires = (_expires),				\
		.data = (_data),				\
		.flags = (_flags),				\
		__TIMER_LOCKDEP_MAP_INITIALIZER(		\
			__FILE__ ":" __stringify(__LINE__))	\
	}

5、用宏来初始化定时器并赋值

//宏定义
#define DEFINE_TIMER(_name, _function, _expires, _data)		\
	struct timer_list _name =//定义了变量				\
		TIMER_INITIALIZER(_function, _expires, _data)
//实现部分
#define TIMER_INITIALIZER(_function, _expires, _data)		\
	__TIMER_INITIALIZER((_function), (_expires), (_data), 0)
#define __TIMER_INITIALIZER(_function, _expires, _data, _flags) { \
		.entry = { .next = TIMER_ENTRY_STATIC },	\
		.function = (_function),			\
		.expires = (_expires),				\
		.data = (_data),				\
		.flags = (_flags),				\
		__TIMER_LOCKDEP_MAP_INITIALIZER(		\
			__FILE__ ":" __stringify(__LINE__))	\
	}

6、添加定时器

void add_timer(struct timer_list *timer)//参数为定时器结构体指针
{
	BUG_ON(timer_pending(timer));
	mod_timer(timer, timer->expires);//就是设置超时时间,如果只设置一次,执行完毕就结束了
}

7、修改定时器超时时间

  在内核定时器的介绍中我们说到,内核定时器注册开启后,运行一次就不会再运行(相当于自动注销),我们可以重新设置定时器的超时时间,让定时器重复运行。

int mod_timer(struct timer_list *timer, unsigned long expires)
             //变量分别为定时器结构体指针和超时时间
{
	return __mod_timer(timer, expires, false);//具体细节看内核源码
}

8、关闭定时器

/*
函数功能:删除定时器
*/
int del_timer(struct timer_list *timer)
{
	struct timer_base *base;
	unsigned long flags;
	int ret = 0;
	debug_assert_init(timer);
	if (timer_pending(timer)) {
		base = lock_timer_base(timer, &flags);
		ret = detach_if_pending(timer, base, true);
		raw_spin_unlock_irqrestore(&base->lock, flags);
	}
	return ret;
}
/*
函数功能:删除定时器,和上面函数不同的是,该函数用在多处理器(SMP)时,如何有其他的处理器在使用
		  定时器时,该函数会睡眠直到使用完毕再将定时器删除。
*/
int del_timer_sync(struct timer_list *timer)
{
#ifdef CONFIG_LOCKDEP
	unsigned long flags;

	/*
	 * If lockdep gives a backtrace here, please reference
	 * the synchronization rules above.
	 */
	local_irq_save(flags);
	lock_map_acquire(&timer->lockdep_map);
	lock_map_release(&timer->lockdep_map);
	local_irq_restore(flags);
#endif
	/*
	 * don't use it in hardirq context, because it
	 * could lead to deadlock.
	 */
	WARN_ON(in_irq() && !(timer->flags & TIMER_IRQSAFE));
	for (;;) {
		int ret = try_to_del_timer_sync(timer);
		if (ret >= 0)
			return ret;
		cpu_relax();
}

9、转换时间

/*
函数功能:微秒转节拍(usecs_to_jiffies)
*/
static __always_inline unsigned long usecs_to_jiffies(const unsigned int u)
//具体实现如下
static inline unsigned long _usecs_to_jiffies(const unsigned int u)
{
	return (u + (USEC_PER_SEC / HZ) - 1) / (USEC_PER_SEC / HZ);
}
#else
static inline unsigned long _usecs_to_jiffies(const unsigned int u)
{
	return (USEC_TO_HZ_MUL32 * u + USEC_TO_HZ_ADJ32)
		>> USEC_TO_HZ_SHR32;
}
/*
函数功能:毫秒转节拍(msecs_to_jiffies)
*/
static __always_inline unsigned long msecs_to_jiffies(const unsigned int m)

三、内核定时器的使用步骤

1、定义时间结构体变量。
2、编写定时器时间到处理函数。
3、初始化定时器,并赋值。
4、删除定时器
技巧:如果定时器想一直开启,可以将修改定时器时间放在定时器时间到处理函数中。
  
  
  

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!