QQ登录

只需一步,快速开始

登录 | 立即注册 | 找回密码

主题

帖子

荣誉

成熟鱼友Ⅰ

Rank: 5Rank: 5Rank: 5

积分
537
查看: 194|回复: 0

[技术交流] 内核定时器

[复制链接]
最佳答案
0 
累计签到:529 天
连续签到:2 天
练家志 发表于 2017-9-26 16:53:07 1940 | 显示全部楼层 |阅读模式

马上注册加入鱼C,享用更多服务吧^_^

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
软件上的定时器最终要依靠硬件时钟来实现,简单的说,内核会在时钟中断发生后检测各个注册到内核的定时器是否到期,如果到期,就回调相应的注册函数,将其作为中断底半部来执行。实际上,时钟中断处理程序会触发TIMER_SOFTIRQ软中断,运行当前处理器上到期的所有定时器。
设备驱动程序如要获得时间信息以及需要定时服务,都可以使用内核定时器。
jiffies
要说内核定时器,首先就得说说内核中关于时间的一个重要的概念:jiffies变量,作为内核时钟的基础,jiffies每隔一个固定的时间就会增加1,称为增加一个节拍,这个固定间隔由定时器中断来实现,每秒中产生多少个定时器中断,由在中定义的HZ宏来确定,如此,可以通过jiffies获取一段时间,比如jiffies/HZ表示自系统启动的秒数。下两秒就是(jiffies/HZ+2),内核中用jiffies来计时,秒转换成的jiffies:seconds*HZ,所以以jiffiy为单位,以当前时刻为基准计时2秒:(jiffies/HZ+2)*HZ=jiffies+2*HZ如果要获取当前时间,可以使用do_gettimeofday(),该函数填充一个struct timeval结构,有着接近微妙的分辨率。
//kernel/time/timekeeping.c
473/**
474  * do_gettimeofday - Returns the time of day in a timeval
475  * @tv:         pointer to the timeval to be set
476  *
477  * NOTE: Users should be converted to using getnstimeofday()
478  */
479 void do_gettimeofday(struct timeval *tv)
驱动程序为了让硬件有足够的时间完成一些任务,常常需要将特定的代码延后一段时间来执行,根据延时的长短,内核开发中使用长延时和短延时两个概念。长延时的定义为:延时时间>多个jiffies,实现长延时可以用查询jiffies的方法:
time_before(jiffies, new_jiffies);
time_after(new_jiffiesmjiffies);
**短延时的定义为:延迟事件接近或短于一个jiffy,实现短延时可以调用
udelay();
mdelay();
这两个函数都是忙等待函数,大量消耗CPU时间,前者使用软件循环来延迟指定数目的微妙数,后者使用前者的嵌套来实现毫秒级的延时。
定时器
驱动可以注册一个内核定时器,来指定一个函数在未来某个时间来执行。定时器从注册到内核开始计时,达到指定的时间后会执行注册的函数。即超时值是一个jiffies值,当jiffies值大于timer->expires时,timer->function就会被执行。API如下
//定一个定时器
struct timer_list my_timer;
//初始化定时器
void init_timer(struct timer_list *timer);
mytimer.function = my_function;
mytimer.expires = jiffies +HZ;
//增加定时器
void add_timer(struct timer_list *timer);
//删除定时器
int del_tiemr(struct timer_list *timer);
实例
static struct timer_list tm;
struct timeval oldtv;
void callback(unsignedlong arg)
{   
struct timeval tv;   
char *strp = (char*)arg;   
do_gettimeofday(&tv);   
printk("%s: %ld, %ld\n", __func__,      tv.tv_sec - oldtv.tv_sec,   tv.tv_usec- oldtv.tv_usec);    oldtv = tv;   
tm.expires = jiffies+1*HZ;   
add_timer(&tm);
}
static int __init demo_init(void)
{   
init_timer(&tm);   
do_gettimeofday(&oldtv);   
tm.function= callback;   
tm.data    = (unsignedlong)"hello world";   
tm.expires = jiffies+1*HZ;   
add_timer(&tm);   
return0;
}
延迟工作
除了使用内核定时器完成定时延迟工作,Linux内核还提供了一套封装好的"快捷方式"-delayed_work,和内核定时器类似,其本质也是利用工作队列和定时器实现,
//include/linux/workqueue.h
100struct work_struct {           
101atomic_long_t data;
102struct list_head entry;
103work_func_t func;104
#ifdef CONFIG_LOCKDEP
105struct lockdep_map lockdep_map;
106#endif
107 };
113struct delayed_work {              
114struct work_struct work;
115struct timer_list timer;
116
117/* target workqueue and CPU ->timer uses to queue ->work */
118struct workqueue_struct *wq;
119int cpu;
120 };
struct work_struct
--103-->需要延迟执行的函数, typedef void (work_func_t)(struct work_struct work);
至此,我们可以使用一个delayed_work对象以及相应的调度API实现对指定任务的延时执行
//注册一个延迟执行591staticinlineboolschedule_delayed_work(struct delayed_work *dwork,unsignedlong delay)//注销一个延迟执行2975 boolcancel_delayed_work(struct delayed_work *dwork)
和内核定时器一样,延迟执行只会在超时的时候执行一次,如果要实现循环延迟,只需要在注册的函数中再次注册一个延迟执行函数。
schedule_delayed_work(&work,msecs_to_jiffies(poll_interval));
楼层
跳转到指定楼层

发表回复

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

小甲鱼强烈推荐 上一条 /2 下一条

    移动客户端下载(未启用)
    微信公众号

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备11014136号

Copyright 2018 鱼C论坛 版权所有 All Rights Reserved.

Powered by Discuz! X3.1 Copyright
© 2001-2018 Comsenz Inc.    All Rights Reserved.

小黑屋|手机版|Archiver|鱼C工作室 ( 粤公网安备 44051102000370号 | 粤ICP备11014136号

GMT+8, 2017-12-16 19:00

快速回复 返回顶部 返回列表