DeviceDriver (5): Kernel timer

One: Kernel time management

1. The hardware timer provides a clock source, and the frequency of the clock source can be set. After setting, it can periodically generate a timer interrupt, and the system can use the timer interrupt to time. The frequency of periodic interruption is the system frequency, also called the beat rate, such as 1000Hz, 500Hz, 100Hz is the system beat rate. The system beat rate can be set, and it can be set when configuring the Linux kernel. The default value is 100Hz, which is 10ms, which is represented by "HZ" in the Linux kernel:

# 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. The Linux kernel uses the global variable jiffies to record the number of system beats since the system was started, and jiffies will be initialized to 0 when the system is started.

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

For the overflow of jiffies data, the Linux kernel provides some APIs to deal with:

#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): Generally a value is jiffies, and b value is comparison value. When a exceeds b, it returns true, otherwise it is false. The time_before function returns the opposite value. The function time_after_eq and time_before_eq have one more equal to the judgment condition.

The linux kernel also provides several APIs for converting between jiffies and ms, us, and ns:

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)

Two: core timer

1. The method of using the Linux kernel timer only needs to provide the timeout time (similar to the timing time) and the timeout processing function. When the timeout time is reached, the corresponding timeout processing function will be executed. It should be noted that the Linux kernel timer is not If it runs periodically, it will be closed after the timeout. If you want to realize the cycle, you only need to restart the timer in the timeout processing function.

Linux kernel timer definition:

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. Core timer related API

(1) Initialization

void init_timer(struct timer_list *timer)

(2) Register the timer, and the timer will start running after the kernel is registered

void add_timer(struct timer_list *timer)

(3) Delete the timer, the return value is 0 means the timer has not been activated, otherwise it has been activated

int del_timer(struct timer_list * timer)

(4) Wait for the timer to exit (that is, use up) and then delete it, and it cannot be used in interrupt context

int del_timer_sync(struct timer_list *timer)

(5) Modify the timing value

int mod_timer(struct timer_list *timer, unsigned long expires)

Three: Example

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

 

Guess you like

Origin blog.csdn.net/qq_34968572/article/details/103698005