搜索
bottom↓
回复: 14

ffxz 可能有更好的方法解决定时器溢出(回绕)问题

[复制链接]

出0入0汤圆

发表于 2010-11-2 13:38:34 | 显示全部楼层 |阅读模式
Linux内核中提供了以下四个宏,可有效地解决由于jiffies溢出而造成程序逻辑出错的情况。下面是从Linux Kernel 2.6.7版本中摘取出来的代码:
/*
* These inlines deal with timer wrapping correctly. You are
* strongly encouraged to use them
* 1. Because people otherwise forget
* 2. Because if the timer wrap changes in future you won't have to
* alter your driver code.
*
* time_after(a,b) returns true if the time a is after time b.
*
* Do this with "<0" and ">=0" to only test the sign of the result. A
* good compiler would generate better code (and a really good compiler
* wouldn't care). Gcc is currently neither.
*/
#define time_after(a,b) \
(typecheck(unsigned long, a) && \
typecheck(unsigned long, b) && \
((long)(b) - (long)(a) < 0))
#define time_before(a,b) time_after(b,a)

#define time_after_eq(a,b) \
(typecheck(unsigned long, a) && \
typecheck(unsigned long, b) && \
((long)(a) - (long)(b) >= 0))
#define time_before_eq(a,b) time_after_eq(b,a)

  time_after等比较时间先后的宏背后的原理 (摘自网上的分析!)

  上述time_after等比较时间先/后的宏为什么能够解决jiffies溢出造成的错误情况呢?
我们仍然以8位无符号整型(unsigned char)为例来加以说明。仿照上面的time_after宏,我们可以给出简化的8位无符号整型对应的after宏:
#define uc_after(a, b) ((char)(b) - (char)(a) < 0)

设a和b的数据类型为unsigned char,b为临近8位无符号整型最大值附近的一个固定值254,下面给出随着a(设其初始值为254)变化而得到的计算值:
a b (char)(b) - (char)(a)
254 254 0
255 - 1
0 - 2
1 - 3
...
124 -126
125 -127
126 -128
127 127
128 126
...
252 2
253 1

从上面的计算可以看出,设定b不变,随着a(设其初始值为254)不断增长1,a的取值变化为:
254, 255, (一次产生溢出)
0, 1, ..., 124, 125, 126, 127, 126, ..., 253, 254, 255, (二次产生溢出)
0, 1, ...
...

而(char)(b) - (char)(a)的变化为:
0, -1,
-2, -3, ..., -126, -127, -128, 127, 126, ..., 1, 0, -1,
-2, -3, ...
...

从上面的详细过程可以看出,当a取值为254,255, 接着在(一次产生溢出)之后变为0,然后增长到127之前,uc_after(a,b)的结果都显示a是在b之后,这也与我们的预期相符。但在a取值为127之后,uc_after(a,b)的结果却显示a是在b之前。
从上面的运算过程可以得出以下结论:
使用uc_after(a,b)宏来计算两个8位无符号整型a和b之间的大小(或先/后,before/after),那么a和b的取值应当满足以下限定条件:
. 两个值之间相差从逻辑值来讲应小于有符号整型的最大值。
. 对于8位无符号整型,两个值之间相差从逻辑值来讲应小于128。
从上面可以类推出以下结论:
对于time_after等比较jiffies先/后的宏,两个值的取值应当满足以下限定条件:
两个值之间相差从逻辑值来讲应小于有符号整型的最大值。
对于32位无符号整型,两个值之间相差从逻辑值来讲应小于2147483647。
对于HZ=100,那么两个时间值之间相差不应当超过2147483647/100秒 = 0.69年 = 248.5天。对于HZ=60,那么两个时间值之间相差不应当超过2147483647/60秒 = 1.135年。在实际代码应用中,需要比较先/后的两个时间值之间一般都相差很小,范围大致在1秒 ~1天左右,所以以上time_after等比较时间先/后的宏完全可以放心地用于实际的代码中。

=================================================================================
用这种方法实现就会轻松解决了定时器的回绕问题使程序逻辑出错,代码也简洁很多!

阿莫论坛20周年了!感谢大家的支持与爱护!!

你熬了10碗粥,别人一桶水倒进去,淘走90碗,剩下10碗给你,你看似没亏,其实你那10碗已经没有之前的裹腹了,人家的一桶水换90碗,继续卖。说白了,通货膨胀就是,你的钱是挣来的,他的钱是印来的,掺和在一起,你的钱就贬值了。

出0入0汤圆

发表于 2010-11-2 15:08:37 | 显示全部楼层
没看明白。

上面说的是a与b的比较,但RT-Thread中的tick比较并不是这样的。如果按照这个方法,能否直接提供个OS Tick问题的patch?

出0入0汤圆

 楼主| 发表于 2010-11-2 16:06:24 | 显示全部楼层
哈哈,由你更改会好一些,我对你的系统不太熟悉!还等着你的radio板来学学!
我举个例子:下面是rrt的Timer代码一部分;
void rt_timer_check(void)
{
        struct rt_timer *t;
        rt_tick_t current_tick;   //rt_tick_t为uint32_t
        register rt_base_t level;

#ifdef RT_TIMER_DEBUG
        rt_kprintf("timer check enter\n");
#endif

        current_tick = rt_tick_get();

        /* disable interrupt */
        level = rt_hw_interrupt_disable();

        while (!rt_list_isempty(&rt_timer_list))
        {
                t = rt_list_entry(rt_timer_list.next, struct rt_timer, list);
                if (current_tick >= t->timeout_tick)  //此处就定时器溢出的逻辑错误(当t->timeout_tick产生回绕时)虽然时间相当长。可以改成:if( (rt_int32_t)current_tick - (rt_int32_t)t->timeout_tick >= 0 ) 通过强制成有符号量进行比较就没有问题了.
                {
#ifdef RT_USING_HOOK
                        if (rt_timer_timeout_hook != RT_NULL) rt_timer_timeout_hook(t);
#endif

                        /* remove timer from timer list firstly */
                        rt_list_remove(&(t->list));

                        /* call timeout function */
                        t->timeout_func(t->parameter);

                        /* re-get tick */
                        current_tick = rt_tick_get();

#ifdef RT_TIMER_DEBUG
                        rt_kprintf("current tick: %d\n", current_tick);
#endif

                        if ((t->parent.flag & RT_TIMER_FLAG_PERIODIC) &&
                                        (t->parent.flag & RT_TIMER_FLAG_ACTIVATED))
                        {
                                /* start it */
                                t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
                                rt_timer_start(t);
                        }
                        else
                        {
                                /* stop timer */
                                t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
                        }
                }
                else break;
        }

        /* enable interrupt */
        rt_hw_interrupt_enable(level);

        /* increase soft timer tick */
#ifdef RT_USING_TIMER_SOFT
        rt_soft_timer_tick_increase ( );
#endif

#ifdef RT_TIMER_DEBUG
        rt_kprintf("timer check leave\n");
#endif
}

//////////////////////////////////////////////////////////////////////////////

把if (current_tick >= t->timeout_tick)  改成:if( (rt_int32_t)current_tick - (rt_int32_t)t->timeout_tick >= 0 )
时,当t->timeout_tick 溢出产生回绕时,用符号运算还能正常得出结果。其原因是current_tick - t->timeout_tick会产生大于
2147483647的数,对有符号运算来说,其值为负值。其结果还是对的。

这样两个值的取值应当满足以下限定条件即可:  
两个值之间相差从逻辑值来讲应小于有符号整型的最大值。  
对于32位无符号整型,两个值之间相差从逻辑值来讲应小于2147483647。  
对于小于2147483647值来说,是很长很长的定时,

出0入0汤圆

发表于 2010-11-2 16:17:12 | 显示全部楼层
有些明白了,多谢。我再仔细思考下应该如何修改

出0入0汤圆

 楼主| 发表于 2010-11-2 16:59:16 | 显示全部楼层
不客气!^:^

出0入0汤圆

发表于 2010-11-2 17:13:48 | 显示全部楼层
改成 if ( (rt_int32_t)(rt_uint32_t)(current_tick-t->timeout_tick) >= 0 )
效率可能好一点,原理是一样的

出0入0汤圆

 楼主| 发表于 2010-11-2 17:16:28 | 显示全部楼层
看不出对效率有何改善?snoopyzz说说!

出0入0汤圆

发表于 2010-11-2 17:24:28 | 显示全部楼层
对于 有符号数的减法运算,编译器生成的代码和无符号数是不一样的

实事上,由于数字在ram存储是没有符号的,负数是补码形式保存,所以无符号数相减得到的差值 就是差值,
只要不产生数据类型所能表示的最大范围差值即可,
我们需要比较的仅仅是后一个tick比前一个大,那么,只需要将这个差值转为有符号数判断符号即可,


LS可以反汇编看看结果是不是我的写法效率高些,8位机做久了...效率一向太重视了,优化写法是家常便饭 -_-

这种比较方式我经常用

不过 ,arm是不是有 有符号32位减法指令?我对arm指令集不熟,有的话,估计效率差不了太多

出0入0汤圆

发表于 2010-11-2 17:33:09 | 显示全部楼层
测试了一下,果然,在arm中两种写法生成的汇编一样-_-
我多事了...

出0入0汤圆

 楼主| 发表于 2010-11-2 17:36:00 | 显示全部楼层
哦!我试试看.

出0入0汤圆

 楼主| 发表于 2010-11-2 17:37:38 | 显示全部楼层
一样的,我就不用试了!^:^

出0入0汤圆

 楼主| 发表于 2010-11-2 21:08:30 | 显示全部楼层
我试着更改了clock.c和timer.c的代码,不知到是否有问题,没有样板板试。我想最好比较做成宏较好,外部也能使用!
clock.c和Timer.c代码ourdev_594716QE00TO.rar(文件大小:4K) (原文件名:clock&Timer.rar)

出0入0汤圆

发表于 2010-11-2 22:00:17 | 显示全部楼层
学习

出0入0汤圆

发表于 2010-11-4 14:45:33 | 显示全部楼层
学习

出0入0汤圆

发表于 2013-7-7 23:28:06 | 显示全部楼层
收藏!好帖!学习了.
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-10-3 01:22

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

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