关于hrtimer_forward小段代码的分析【转】
转自:http://blog.csdn.net/wowuyinglingluan/article/details/45720151
版权声明:本文为博主原创文章,未经博主允许不得转载。
随着各种嵌入式设备上采用linux,特别是Android系统的广泛应用,linux的hrtimer高精度模式开始被广泛支持。当然,虽说可以支持到ns精度,具体实现依赖于硬件定时器和内核编译条件,不过,一般情况下,几十us的定时精度都是支持的。这给编写驱动带来了很大的方便。
之前有一篇非常强大的博文Linux时间子系统之六:高精度定时器(HRTIMER)的原理和实现,已经将hrtimer的基本原理和hrtimer的应用方法做了清晰详尽的剖析,这里针对在应用hrtimer时,往往导致定时器没有按照设计精度执行的问题,分析一下hrtimer_forword的内部的小段代码。
整段代码
817 u64 hrtimer_forward(struct hrtimer *timer, ktime_t now, ktime_t interval)
818 {
819 u64 orun = 1;
820 ktime_t delta;
821
822 delta = ktime_sub(now, hrtimer_get_expires(timer));
823
824 if (delta.tv64 < 0)
825 return 0;
826
827 if (interval.tv64 < timer->base->resolution.tv64)
828 interval.tv64 = timer->base->resolution.tv64;
829
830 if (unlikely(delta.tv64 >= interval.tv64)) {
831 s64 incr = ktime_to_ns(interval);
832
833 orun = ktime_divns(delta, incr);
834 hrtimer_add_expires_ns(timer, incr * orun);
835 if (hrtimer_get_expires_tv64(timer) > now.tv64)
836 return orun;
837 /*
838 * This (and the ktime_add() below) is the
839 * correction for exact:
840 */
841 orun++;
842 }
843 hrtimer_add_expires(timer, interval);
844
845 return orun;
846 }
关于无效的forward
以上是3.4版本内核中的hrtimer_forward的实现,我们来分析一下这里面的几个关键环节:
首先时给出的now,这个时间很重要,
822 delta = ktime_sub(now, hrtimer_get_expires(timer));
823
824 if (delta.tv64 < 0)
825 return 0;
因为如上我们可以看到,如果当前时间比定时器到期时间要早,说明定时器上还有一个工作需要在当前时间完成,因此,不会进行任何操作。直接返回,也就是说,如果我们希望这次hrtimer_forward起作用,那么实际上,不会起任何作用。
关于定时精度问题
接下来看看定时精度,如果我们看一下针对某一嵌入式系统的linux源码中hrtimer的实现,我们就会看到,良好的实现均准确地设置了resolution,这个resolution是定时器能够实现的最高精度,当然这一般不是硬件决定的(一个主频48Mhz的MCU,其硬件定时器的定时精度都在20ns),而是嵌入式系统设计者根据系统的整体性能考虑权衡的结果。
827 if (interval.tv64 < timer->base->resolution.tv64)
828 interval.tv64 = timer->base->resolution.tv64;
因此我们看到,如果我们给出的interval如果比这个精度要高,那么,我们只能得到最短等于该精度的定时。
精确调整和overrun问题
接下去的代码,还有点儿让对gcc不太了解的人糊涂,就是
830 if (unlikely(delta.tv64 >= interval.tv64)) {
这句话的关节在于unlikely宏,其实这是在提醒gcc编译器,一般情况下,delta.tv64 >= interval.tv64的条件按是不成立的,这可以让gcc编译出更加高效的代码。并不影响if内部的判断,我们可以直接理解为:
if (delta.tv64 >= interval.tv64) {
而在这个条件为真的时候,会对expires作较为精密的外科手术。delta是一个由于hrtimer自身遍历以及我们callback函数内部的业务处理,耗费了一定时间,已经超过了 interval,那么则认为出现了overrun现象,即下一次定时已经被覆盖掉了。这时,内核希望做一些补救工作,不过也做不了多少,就是告诉你按照你给出interval,已经超时了几次了,并且看看是否可以根据这个来微调后续的定时时间。这里的代码挺有意思,我们连起来看一下
/*这是delta怎么来的*/
822 delta = ktime_sub(now, hrtimer_get_expires(timer));
831 s64 incr = ktime_to_ns(interval);
832 /*这是一个将当前的expires加上detal所能包含的interval整数倍的时间的计算
833 orun = ktime_divns(delta, incr);
834 hrtimer_add_expires_ns(timer, incr * orun);
那么,如果hrtimer的处理,是在硬件中断上的原子性操作,且ktime_divns只是简单的整数除法,以下的代码绝对不可能成立:
835 if (hrtimer_get_expires_tv64(timer) > now.tv64)
836 return orun;
那么,实际上,它是一个通过损失精度来完成计算的除法,如果除数大于32位,那么除数与被除数一起损失精度到除数小于等于32位为止,以下是其函数定义:
u64 ktime_divns(const ktime_t kt, s64 div)
313 {
314 u64 dclc;
315 int sft = 0;
316
317 dclc = ktime_to_ns(kt);
318 /* Make sure the divisor is less than 2^32: */
319 while (div >> 32) {
320 sft++;
321 div >>= 1;
322 }
323 dclc >>= sft;
324 do_div(dclc, (unsigned long) div);
325
326 return dclc;
327 }
因此,如果inteval是一个大于2^32的大数的时候,还真有可能让条件成立滴^_^。这是返回值和加载expires上的值都对,没有什么要讲的了。
如果不成立,则
841 orun++;
842 }
843 hrtimer_add_expires(timer, interval);
存疑
那么在这样的一个状态下,很不幸,expires被加上了(incr * orun+interval)这么多的时间,我们可以理解为,此时,内核认为你希望得到的定时时间为interval给定定时时间的整数倍,然后你还知道有几次overrun,这个主意不坏,但是我们带入实际的数值,就会感觉比较好玩了。
假设:
delta=5ns,interval=4ns
则:
行833计算结果:orrun=1
行834在原expires上加上了4ns
那么,由于行835此时条件不会为真那么在:
行841,orrun变成了2
然后在行843,expires的值加上了4ns
至此,expires一共加上了8ns,如果所有其他操作瞬间完成的话,将在3ns后得到下一次callback的机会。
如果按照这样的逻辑,我们看到返回的结果凌乱了,明明overrun了一次,结果会返回2。如果我没有理解错误的话,此时的overrun不能反应实际的情况了
关于hrtimer_forward小段代码的分析【转】的更多相关文章
- js中闭包来实现bind函数的一段代码的分析
今天研究了一下bind函数,发现apply和call还可以有这样的妙用,顺便巩固复习了闭包. var first_object = { num: 42 }; var second_object = { ...
- matlab小段代码学习
matlab读hdf文件到txt filename='E:\data\H1BDLD10110607231863921.L2B.HDF'; h=hdftool(filename); Latitude = ...
- SVNKIT一段代码的分析
打印SVNkit版本库中的结构: 函数如下: 调用方法如下: listEntries(repository, ""); System.out.println("XXXXX ...
- python优势之通过一段代码来了解python的强大之处
晚上闲暇之余随意翻了一下博客,看到https://www.jianshu.com/p/69bf0ed0b5cc作者提到了一段代码,刚开始看没啥感觉,仔细深入后引起了我的注意.里面使用了python最简 ...
- flappy pig小游戏源码分析(1)——主程序初探
闲逛github发现一个javascript原生实现的小游戏,源码写的很清晰,适合想提高水平的同学观摩学习.读通源码后,我决定写一系列的博客来分析源码,从整体架构到具体实现细节来帮助一些想提高水平的朋 ...
- 小程序代码包压缩 策略&方案
微信小程序自推出以来,逐渐发展,目前正受到越来越多的青睐.其中很重要的一点得益于小程序的轻量级特性,每个小程序最多不超过2MB,招之即来挥之即去,相比于几十上百兆的APP,用户进入小程序,或者说,小程 ...
- HashMap的小总结 + 源码分析
一.HashMap的原理 所谓Map,就是关联数组,存的是键值对——key&value. 实现一个简单的Map,你也许会直接用两个LIst,一个存key,一个存value.然后做查询或者get ...
- STM32启动代码详细分析
最近需要学习iap的功能,因此离不开stm32的启动代码的分析,以前看了很多遍,都看不懂,读书百遍,其义自见,因此我有看了一遍,下面的文章,挺好的,因此转载: 在上电复位后,我们都知道会先运行启动代码 ...
- 微信小程序应用安全分析及设计
针对微信关于小程序安全设计的分析 针对微信小程序开发配置及部分配置机制分析微信小程序安全设计: AppSecret 管理员生成AppSecret,在与微信后台交互过程中部分接口使用,如 auth.co ...
随机推荐
- (vue01)vue环境搭建
腾讯,百度,网易....这么大媒体平台咋老推送这么lower的信息? 你们不缺钱啊....我这么善良的孩子都别你们带坏了 强烈鄙视马化腾 强烈鄙视李彦宏 参考地址: https://segmentfa ...
- KVM web管理工具——WebVirtMgr(一)
WebVirtMgr 介绍 WebVirtMgr采用几乎纯Python开发,其前端是基于Python的Django,后端是基于Libvirt的Python接口,将日常kvm的管理操作变的更加的 ...
- MySQL训练营01
一.数据库基础知识: 1. 数据库(database):保存有组织的数据的容器(通常是一个或者一组文件) 2. 数据库管理系统(DBMS):数据库软件,外界通过DBMS来创建和操纵数据库,具体是什么, ...
- [leetcode-609-Find Duplicate File in System]
https://discuss.leetcode.com/topic/91430/c-clean-solution-answers-to-follow-upGiven a list of direct ...
- Linux SPI总线和设备驱动架构之三:SPI控制器驱动
通过第一篇文章,我们已经知道,整个SPI驱动架构可以分为协议驱动.通用接口层和控制器驱动三大部分.其中,控制器驱动负责最底层的数据收发工作,为了完成数据的收发工作,控制器驱动需要完成以下这些功能:1. ...
- beta版本冲刺五
目录 组员情况 组员1(组长):胡绪佩 组员2:胡青元 组员3:庄卉 组员4:家灿 组员5:恺琳 组员6:翟丹丹 组员7:何家伟 组员8:政演 组员9:黄鸿杰 组员10:刘一好 组员11:何宇恒 展示 ...
- 《Hadoop基础教程》之初识Hadoop(转载)
转载自博主:上善若水任方圆http://blessht.iteye.com/blog/2095675 Hadoop一直是我想学习的技术,正巧最近项目组要做电子商城,我就开始研究Hadoop,虽然最后鉴 ...
- 我的python计划
一直想学习一种脚本语言.现在主流的脚本语言,比较先接触的是python 刚开始了解了一下python,感觉挺适合自己的感觉,学习了一段时间,之中感觉,就好象C++一样,把面向对象和面向过程编程结合了起 ...
- 【Python】PYTHON九九乘法表
python2.7 for i in range(1,10): for j in range(1,i+1): print j,'x',i,'=',j*i,'\t', print '\n'pr ...
- CSS的基本使用
CSS的出现就是为了将HTML的内容与样式分离 CSS的书写方式 selector{ key:value } h1{ color: blue; } <!DOCTYPE html> < ...