Linux內核定時器

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、刪除定時器
技巧:如果定時器想一直開啓,可以將修改定時器時間放在定時器時間到處理函數中。
  
  
  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章