参考链接1
参考链接2
利用定时器,我们可以设定在未来的某一时刻,触发一个特定的事件。所谓低精度定时器,是指这种定时器的计时单位基于jiffies值的计数,也就是说,它的精度只有1/HZ,假如你的内核配置的HZ是1000,那意味着系统中的低分辨率定时器的精度就是1ms。早期的内核版本中,内核并不支持高精度定时器,理所当然只能使用这种低分辨率定时器,我们有时候把这种基于HZ的定时器机制称为时间轮:time wheel。虽然后来出现了高分辨率定时器,但它只是内核的一个可选配置项,所以直到目前最新的内核版本,这种低精度定时器依然被大量地使用着。
想要使用低精度定时器,主要需要定义并填充一个 sruct timer_list 结构体,如下
struct timer_list {
/*
* All fields that change during normal runtime grouped to the
* same cacheline
*/
struct list_head entry; //内核使用
unsigned long expires; // 需要填写期望的jiffies计数值
struct tvec_base *base; // 内核使用
void (*function)(unsigned long); // 回调函数指针, 定时器完成时被调用
unsigned long data; // 回调函数的参数
int slack;
......
};
要定义一个timer_list,我们可以使用静态和动态两种办法,静态方法使用DEFINE_TIMER宏:
#define DEFINE_TIMER(_name, _function, _expires, _data) \
struct timer_list _name = \
TIMER_INITIALIZER(_function, _expires, _data)
该宏将得到一个名字为 _name,并分别用 _function , _expires,_data参数填充timer_list的相关字段。
如果要使用动态的方法,则可以自己声明一个timer_list结构,然后手动初始化它的各个字段:
struct timer_list timer; //定义timer_list
......
init_timer(&timer); // 初始化timer
timer.function = _function;
timer.expires = _expires;
timer.data = _data; // 回调函数的参数数据
这里只是简单列出了几个常用的API,具体的可以查看源代码 :
init_timer(&timer); 初始化一个定时器
#define init_timer(timer) \
__init_timer((timer), 0)
add_timer(&timer); 激活一个定时器
/**
* add_timer - start a timer
* @timer: the timer to be added
*
* The kernel will do a ->function(->data) callback from the
* timer interrupt at the ->expires point in the future. The
* current time is 'jiffies'.
*
* The timer's ->expires, ->function (and if the handler uses it, ->data)
* fields must be set prior calling this function.
*
* Timers with an ->expires field in the past will be executed in the next
* timer tick.
*/
void add_timer(struct timer_list *timer)
{
BUG_ON(timer_pending(timer));
mod_timer(timer, timer->expires);
}
EXPORT_SYMBOL(add_timer);
mod_timer(&timer, jiffies+50); 更改定时器到期的时间
/**
* mod_timer - modify a timer's timeout
* @timer: the timer to be modified
* @expires: new timeout in jiffies
*
* mod_timer() is a more efficient way to update the expire field of an
* active timer (if the timer is inactive it will be activated)
*
* mod_timer(timer, expires) is equivalent to:
*
* del_timer(timer); timer->expires = expires; add_timer(timer);
*
* Note that if there are multiple unserialized concurrent users of the
* same timer, then mod_timer() is the only safe way to modify the timeout,
* since add_timer() cannot modify an already running timer.
*
* The function returns whether it has modified a pending timer or not.
* (ie. mod_timer() of an inactive timer returns 0, mod_timer() of an
* active timer returns 1.)
*/
int mod_timer(struct timer_list *timer, unsigned long expires)
{
expires = apply_slack(timer, expires);
/*
* This is a common optimization triggered by the
* networking code - if the timer is re-modified
* to be the same thing then just return:
*/
if (timer_pending(timer) && timer->expires == expires)
return 1;
return __mod_timer(timer, expires, false, TIMER_NOT_PINNED);
}
EXPORT_SYMBOL(mod_timer);
del_timer(&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;
}
EXPORT_SYMBOL(del_timer);
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#undef debug
#define TAG "zyk-module"
#define debug
#ifdef debug
#define zyk_info(fmt, ...) pr_info("zyk "TAG" V %-4d %s "fmt"\n",__LINE__, __func__,##__VA_ARGS__)
#define zyk_warn(fmt, ...) pr_warn("zyk "TAG" W %-4d %s "fmt"\n",__LINE__, __func__,##__VA_ARGS__)
#define zyk_err(fmt, ...) pr_err ("zyk "TAG" E %-4d %s "fmt"\n",__LINE__, __func__,##__VA_ARGS__)
#else
#define zyk_info(fmt, ...) pr_info("")
#define zyk_warn(fmt, ...) pr_warn("")
#define zyk_err(fmt, ...) pr_err ("zyk "TAG" E %-4d %s "fmt"\n",__LINE__, __func__,##__VA_ARGS__)
#endif
#define DELAY_TIME 20
static short enable = 1;
static short time_ok = 0;
static struct timer_list poll_timer;
static wait_queue_head_t wait_queue;
static struct task_struct * print_utc_tsk;
static int print_utc_thread_func(void * data)
{
struct timeval time;
struct rtc_time rtc_time;
do{
wait_event_interruptible(wait_queue,(enable == 1 )&& (time_ok==1));
time_ok = 0;
do_gettimeofday(&time);
rtc_time_to_tm(time.tv_sec - sys_tz.tz_minuteswest * 60, &rtc_time);
zyk_info("Android UTC Time :%04d-%02d-%02d %02d:%02d:%02d.%04lu",rtc_time.tm_year+1900,\
rtc_time.tm_mon+1,\
rtc_time.tm_mday,
rtc_time.tm_hour,\
rtc_time.tm_min,\
rtc_time.tm_sec,\
(unsigned long)time.tv_usec);
// msleep(DELAY_TIME*1000);
mod_timer(&poll_timer,jiffies + DELAY_TIME*HZ );
}while(!kthread_should_stop());
return 0;
}
static int print_time_enable(const char * val ,const struct kernel_param *kp)
{
int ret = param_set_short(val,kp);
if(ret)
return ret ;
wake_up_interruptible(&wait_queue);
return ret;
}
static struct kernel_param_ops enable_param_ops = {
.set = print_time_enable,
.get = param_get_short,
};
module_param_cb(enable,&enable_param_ops,&enable,0664);
MODULE_PARM_DESC(enable,"print utc time enable");
void timer_handle(unsigned long timer)
{
time_ok = 1;
wake_up_interruptible(&wait_queue);
}
static int zyk_module_platform_probe(struct platform_device * pdev)
{
int ret=0;
zyk_info("+");
init_waitqueue_head(&wait_queue);
print_utc_tsk = kthread_run(print_utc_thread_func,NULL,"print_utc");
if(IS_ERR(print_utc_tsk)){
zyk_err(" thread create fail ! ");
ret = -EPERM;
}
init_timer(&poll_timer);
poll_timer.expires = jiffies+ DELAY_TIME*HZ; //20 s
poll_timer.function = timer_handle;
poll_timer.data = 1;
add_timer(&poll_timer);
return ret;
}
static int zyk_module_platform_remove(struct platform_device * pdev)
{
int ret=0;
zyk_info("+");
return ret;
}
static void zyk_module_platform_shutdown(struct platform_device * pdev)
{
zyk_info("+");
}
static const struct of_device_id zyk_module_of_match[] = {
{
.compatible = "zyk"
},
{},
};
static struct platform_device zyk_module_platform_device = {
.name = "zyk_test" ,
.id = -1,
};
static struct platform_driver zyk_module_platform_driver = {
.probe = zyk_module_platform_probe,
.remove = zyk_module_platform_remove,
.shutdown = zyk_module_platform_shutdown,
.driver = {
.name = "zyk_test",
.owner = THIS_MODULE,
.of_match_table = zyk_module_of_match,
}
};
static int __init zyk_module_init(void)
{
int ret=0;
ret = platform_driver_register(&zyk_module_platform_driver);
if( ret < 0 ){
zyk_err("driver register fail");
}
ret = platform_device_register(&zyk_module_platform_device);
if( ret < 0 ){
zyk_err("device register fail");
}
zyk_info("+");
return ret;
}
static void __exit zyk_module_exit(void)
{
zyk_info("+");
}
module_init(zyk_module_init);
module_exit(zyk_module_exit);
MODULE_AUTHOR("[email protected]");
MODULE_LICENSE("GPL");