https://www.cnblogs.com/chen-farsight/p/6226562.html
linux内核定时器是一个结构体timer_list,定时器挂载在内核定时器链表上。
4.1.45版本内核:
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;
#ifdef CONFIG_TIMER_STATS
int start_pid;
void *start_site;
char start_comm[16];
#endif
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
4.15.1内核版本:
struct timer_list {
/*
* All fields that change during normal runtime grouped to the
* same cacheline
*/
struct hlist_node entry;
unsigned long expires;
void (*function)(struct timer_list *);
u32 flags;
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
可以看到timer_list结构体相比较老版本,没有了data参数。
初始化定时器可以用init_timer()函数,或者timer_setup()函数
init_timer()函数
大爷的,一个init_timer()函数搞这么复杂,绕这么多弯。
#define init_timer(timer) \
__init_timer((timer), 0)
#ifdef CONFIG_LOCKDEP
#define __init_timer(_timer, _flags) \
do { \
static struct lock_class_key __key; \
init_timer_key((_timer), (_flags), #_timer, &__key); \
} while (0)
#else
#define __init_timer(_timer, _flags) \
init_timer_key((_timer), (_flags), NULL, NULL)
/**
* init_timer_key - initialize a timer
* @timer: the timer to be initialized
* @flags: timer flags
* @name: name of the timer
* @key: lockdep class key of the fake lock used for tracking timer
* sync lock dependencies
*
* init_timer_key() must be done to a timer prior calling *any* of the
* other timer functions.
*/
void init_timer_key(struct timer_list *timer, unsigned int flags,
const char *name, struct lock_class_key *key)
{
debug_init(timer);
do_init_timer(timer, flags, name, key);
}
static void do_init_timer(struct timer_list *timer, unsigned int flags,
const char *name, struct lock_class_key *key)
{
struct tvec_base *base = raw_cpu_read(tvec_bases);
timer->entry.next = NULL;
timer->base = (void *)((unsigned long)base | flags);
timer->slack = -1;
#ifdef CONFIG_TIMER_STATS
timer->start_site = NULL;
timer->start_pid = -1;
memset(timer->start_comm, 0, TASK_COMM_LEN);
#endif
lockdep_init_map(&timer->lockdep_map, name, key, 0);
}
init_timer()函数中就是base,slack等不重要的乱七八糟的给初始化了。
关于timer_setup()函数,
老版本中是setup_timer():
#define setup_timer(timer, fn, data) \
__setup_timer((timer), (fn), (data), 0)
https://www.cnblogs.com/chen-farsight/p/6226562.html
新版本中setup_timer函数变成了timer_setup函数,timer_setup函数使用见:
https://blog.csdn.net/tiantao2012/article/details/79324154
关于timer_setup宏:
/**
* timer_setup - prepare a timer for first use
* @timer: the timer in question
* @callback: the function to call when timer expires
* @flags: any TIMER_* flags
*
* Regular timer initialization should use either DEFINE_TIMER() above,
* or timer_setup(). For timers on the stack, timer_setup_on_stack() must
* be used and must be balanced with a call to destroy_timer_on_stack().
*/
#define timer_setup(timer, callback, flags) \
__init_timer((timer), (callback), (flags))
#ifdef CONFIG_LOCKDEP
#define __init_timer(_timer, _fn, _flags) \
do { \
static struct lock_class_key __key; \
init_timer_key((_timer), (_fn), (_flags), #_timer, &__key);\
} while (0)
#else
#define __init_timer(_timer, _fn, _flags) \
init_timer_key((_timer), (_fn), (_flags), NULL, NULL)
#endif
其实两种初始化方法差不多,最后都调用了init_timer_key()函数,只不过timer_setup宏多了一个回调函数参数。
注意,无论用哪种方法初始化,其本质都只是给字段赋值,所以只要在运行 add_timer() 之前,expires, function 和 data 字段都可以直接再修改。
/* 实现每隔一秒向内核log中打印一条信息 */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/time.h>
#include <linux/timer.h>
static struct timer_list tm;
struct timeval oldtv;
void callback(struct timer_list *arg)
{
struct timeval tv;
printk("--%s: %lu--\n", __func__, jiffies);
do_gettimeofday(&tv);
printk("%s: %ld, %ld\n", __func__,
tv.tv_sec - oldtv.tv_sec, //与上次中断间隔 s
tv.tv_usec- oldtv.tv_usec); //与上次中断间隔 ms
oldtv = tv;
tm.expires = jiffies+1*HZ;
add_timer(&tm); //重新开始计时
}
static int __init demo_init(void)
{
printk(KERN_INFO "%s : %s : %d - ok.\n", __FILE__, __func__, __LINE__);
timer_setup(&tm,callback,0);
do_gettimeofday(&oldtv); //获取当前时间
tm.expires = jiffies+1*HZ; //定时时间
add_timer(&tm); //注册定时器
return 0;
}
static void __exit demo_exit(void)
{
printk(KERN_INFO "%s : %s : %d - ok.\n", __FILE__, __func__, __LINE__);
del_timer(&tm); //注销定时器
}
module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Farsight");
MODULE_DESCRIPTION("Demo for kernel module");
https://www.iteye.com/blog/allenshao-982270
https://www.jb51.net/article/126465.htm
del_timer(struct timer_list *timer)
注销一个定时器,可以通过 del_timer(struct timer_list *timer) 或 del_timer_sync(struct timer_list *timer) 。
/**
* del_timer - deactive a timer.
* @timer: the timer to be deactivated
*
* del_timer() deactivates a timer - this works on both active and inactive
* timers.
*
* The function returns whether it has deactivated a pending timer or not.
* (ie. del_timer() of an inactive timer returns 0, del_timer() of an
* active timer returns 1.)
*/
int del_timer(struct timer_list *timer)
{
struct tvec_base *base;
unsigned long flags;
int ret = 0;
debug_assert_init(timer);
timer_stats_timer_clear_start_info(timer);
if (timer_pending(timer)) {
base = lock_timer_base(timer, &flags);
ret = detach_if_pending(timer, base, true);
spin_unlock_irqrestore(&base->lock, flags);
}
return ret;
}
其中 del_timer_sync 是用在 SMP 系统上的(在非SMP系统上,它等于del_timer),当要被注销的定时器函数正在另一个 cpu 上运行时,del_timer_sync() 会等待其运行完,所以这个函数会休眠。另外还应避免它和被调度的函数争用同一个锁。对于一个已经被运行过且没有重新注册自己的定时器而言,注销函数其实也没什么事可做。
jiffies:
jiffies是内核中的一个全局变量,用来记录自系统启动一来产生的节拍数。譬如,如果计算系统运行了多长时间,可以用 jiffies/tick rate 来计算。
https://blog.csdn.net/qq_33160790/article/details/78660039
可以使用jiffies来定时。
https://www.cnblogs.com/simonshi/archive/2010/12/13/1694819.html