Linux驱动开发-- 低精度定时器的使用

参考链接1
参考链接2
利用定时器,我们可以设定在未来的某一时刻,触发一个特定的事件。所谓低精度定时器,是指这种定时器的计时单位基于jiffies值的计数,也就是说,它的精度只有1/HZ,假如你的内核配置的HZ是1000,那意味着系统中的低分辨率定时器的精度就是1ms。早期的内核版本中,内核并不支持高精度定时器,理所当然只能使用这种低分辨率定时器,我们有时候把这种基于HZ的定时器机制称为时间轮:time wheel。虽然后来出现了高分辨率定时器,但它只是内核的一个可选配置项,所以直到目前最新的内核版本,这种低精度定时器依然被大量地使用着。

  • 头文件: /linux/timer.h
  • 源文件: /kernel/time/timer.c

一,低精度定时器的使用

1 . struct timer_list 结构体

想要使用低精度定时器,主要需要定义并填充一个 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;  
        ......  
};  

  • entry: 字段用于把一组定时器组成一个链表,至于内核如何对定时器进行分组,我们会在后面进行解释。
  • expires: 字段指出了该定时器的到期时刻,也就是期望定时器到期时刻的jiffies计数值。
  • base 每个cpu拥有一个自己的用于管理定时器的tvec_base结构,该字段指向该定时器所属的cpu所对应tvec_base结构。
  • function 字段是一个函数指针,定时器到期时,系统将会调用该回调函数,用于响应该定时器的到期事件。
  • data 该字段用于上述回调函数的参数。
  • slack 对有些对到期时间精度不太敏感的定时器,到期时刻允许适当地延迟一小段时间,该字段用于计算每次延迟的HZ数。

要定义一个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;  // 回调函数的参数数据 
2. 相关API

这里只是简单列出了几个常用的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);
    
3. 示例: 利用timer_list每隔20s打印一次rtc时间
#include <linux/module.h>
#include <linux/init.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/kthread.h>
#include <linux/wait.h>
#include <linux/mutex.h>


#include <linux/time.h>
#include <linux/delay.h>
#include <linux/rtc.h>


#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("zhaiyk@sina.cn");
MODULE_LICENSE("GPL");

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值