转自: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小段代码的分析【转】的更多相关文章

  1. js中闭包来实现bind函数的一段代码的分析

    今天研究了一下bind函数,发现apply和call还可以有这样的妙用,顺便巩固复习了闭包. var first_object = { num: 42 }; var second_object = { ...

  2. matlab小段代码学习

    matlab读hdf文件到txt filename='E:\data\H1BDLD10110607231863921.L2B.HDF'; h=hdftool(filename); Latitude = ...

  3. SVNKIT一段代码的分析

    打印SVNkit版本库中的结构: 函数如下: 调用方法如下: listEntries(repository, ""); System.out.println("XXXXX ...

  4. python优势之通过一段代码来了解python的强大之处

    晚上闲暇之余随意翻了一下博客,看到https://www.jianshu.com/p/69bf0ed0b5cc作者提到了一段代码,刚开始看没啥感觉,仔细深入后引起了我的注意.里面使用了python最简 ...

  5. flappy pig小游戏源码分析(1)——主程序初探

    闲逛github发现一个javascript原生实现的小游戏,源码写的很清晰,适合想提高水平的同学观摩学习.读通源码后,我决定写一系列的博客来分析源码,从整体架构到具体实现细节来帮助一些想提高水平的朋 ...

  6. 小程序代码包压缩 策略&方案

    微信小程序自推出以来,逐渐发展,目前正受到越来越多的青睐.其中很重要的一点得益于小程序的轻量级特性,每个小程序最多不超过2MB,招之即来挥之即去,相比于几十上百兆的APP,用户进入小程序,或者说,小程 ...

  7. HashMap的小总结 + 源码分析

    一.HashMap的原理 所谓Map,就是关联数组,存的是键值对——key&value. 实现一个简单的Map,你也许会直接用两个LIst,一个存key,一个存value.然后做查询或者get ...

  8. STM32启动代码详细分析

    最近需要学习iap的功能,因此离不开stm32的启动代码的分析,以前看了很多遍,都看不懂,读书百遍,其义自见,因此我有看了一遍,下面的文章,挺好的,因此转载: 在上电复位后,我们都知道会先运行启动代码 ...

  9. 微信小程序应用安全分析及设计

    针对微信关于小程序安全设计的分析 针对微信小程序开发配置及部分配置机制分析微信小程序安全设计: AppSecret 管理员生成AppSecret,在与微信后台交互过程中部分接口使用,如 auth.co ...

随机推荐

  1. ADB常用指令

    adb 命令是adb程序自带的一些命令:adb shell则是调用Android系统的命令,Android系统特有的命令都放在Android设备的/system/bin目录中 MonkeyRunner ...

  2. P3950部落冲突

    题面 \(Solution:\) 法一:LCT裸题 又好想又好码,只不过常数太大. 法二:树链剖分 每次断边将该边权的值++,连边--,然后边权化点权(给儿子),询问就查询从x到y的路径上的边权和,树 ...

  3. 2.爬虫 urlib库讲解 异常处理、URL解析、分析Robots协议

    1.异常处理 URLError类来自urllib库的error模块,它继承自OSError类,是error异常模块的基类,由request模块产生的异常都可以通过这个类来处理. from urllib ...

  4. 一篇文章解决django中时区问题

    首先要明确的是,当在Django项目的setting.py文件中设置了USE_TZ=True时,我们给定的时间存储到数据库的时候都会变成UTC时间(使用auto_now_add和auto_now为Tr ...

  5. [Elasticsearch] 多字段搜索 (三) - multi_match查询和多数字段

    multi_match查询 multi_match查询提供了一个简便的方法用来对多个字段执行相同的查询. NOTE 存在几种类型的multi_match查询,其中的3种正好和在"了解你的数据 ...

  6. ui-grid从后端获取数据后更改数据显示的格式

    从后端获取的数据时是这样的: { "TotalCount":14,"Items": [ { "ProfileId":14, "Na ...

  7. poj 2226 Muddy Fields (二分匹配)

    Muddy Fields Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 7340   Accepted: 2715 Desc ...

  8. 【题解】[国家集训队]Crash的数字表格 / JZPTAB

    求解\(\sum_{i = 1}^{n}\sum_{j = 1}^{m}lcm\left ( i,j \right )\). 有\(lcm\left ( i,j \right )=\frac{ij}{ ...

  9. [ZOJ3899]State Reversing

    [ZOJ3899]State Reversing 试题描述 Yakumo Yukari is with no doubt one of the most powerful youkai in Gens ...

  10. [洛谷P3803] 【模板】多项式乘法(FFT, NTT)

    题目大意:$FFT$,给你两个多项式,请输出乘起来后的多项式. 题解:$FFT$,由于给的$n$不是很大,也可以用$NTT$做 卡点:无 C++ Code:  FFT: #include <cs ...