对 jiffies 溢出、 系统滴答数ticks、time_after 宏的分析理解

(转载请注明出处:http://blog.csdn.net/gujintong1110/article/details/44504343)

最近用到了tick timer系统滴答计时器,遇到了溢出的问题,就是计时一直在增长,总有一天会溢出。

正常情况下两个时间向比较:

jiffies - last_jiffies > 0;如果jiffies溢出,就可能 jiffies - last_jiffies < 0;

那如何解决这个问题呢?网上查了下资料,刚好linux内核的jiffies有个完美的解决方案。源码如下:


/* 
* 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(a,b);假设条件:a,b为无符号整形unsigned char ,取值范围 0-255。

若(a,b) ==> (1,1+1); 得 (char)(b) - (char)(a) ==> 2 - 1 > 0

若(a,b) ==> (255,255+1); 得 (char)(b) - (char)(a) ==> 0 - (-1) ==> 1 > 0

若(a,b) ==> (255,255+127); 得 (char)(b) - (char)(a) ==> 126 - (-1) ==>127 > 0

若(a,b) ==> (127,127+1); 得 (char)(b) - (char)(a) ==> -128 - 127 ==> -255==>1 > 0 (-255的最高位溢出,略去,变成了1)

若(a,b) ==> (127,127+127); 得 char)(b) - (char)(a) ==> -2- 127 ==>-129==>127 > 0 (-129的最高位溢出,略去,变成了127)


上面的计算结果有没有搞晕?一开始我并没有搞清楚上面的计算结果。遂通过代码实际验证之~~~~


<pre name="code" class="html">#include <limits.h>
#include <stdio.h>

/*
* Check at compile time that something is of a particular type.
* Always evaluates to 1 so you may use it easily in comparisons.
typecheck宏有两个参数,
第一个是一个类型,比如unsigned long,
第二个是一个变量,比如a。
它生成一个unsigned long类型的变量__dummy,
然后利用typeof生成和a同样类型的变量__dummy2,
比较__dummy和__dummy2的地址。
如果它们不是同样类型的指针比较,比如a不是unsigned long,
这时候编译器会有一个警告,让你注意到这个问题。
*/
#define typecheck(type,x) \
({        type __dummy; \
        typeof(x) __dummy2; \
        (void)(&__dummy == &__dummy2); \
        1; \
})


#define time_after(a,b) \
(typecheck(unsigned long, a) && \
typecheck(unsigned long, b) && \
((long)(b) - (long)(a) < 0))

/*这个宏可以简化成

*#define typecheck(type,x) \
*((long)(b) - (long)(a) < 0)
*当然,在long == int的编译器中也可以

*((int)(b) - (int)(a) < 0)
*/

int main(int argc, char **argv)
{
	unsigned long a,b;

	printf("char_range: [%-4ld, %-4ld]\n",LONG_MIN,LONG_MAX);
	printf("uchar_range:[%-4lu, %-4lu]\n",0,ULONG_MAX);
	
	a = 1; 
	b = 1;
	printf("a=%u,b=%u, time_after(a,a+b)=%d\n",a, b, time_after(a,a+b));
	

	
	a = ULONG_MAX;
	b = 1;
	printf("a=%u,b=%u, time_after(a,a+b)=%d\n",a, b, time_after(a,a+b));

	a = ULONG_MAX;
	b = ULONG_MAX/2;
	printf("a=%u,b=%u, time_after(a,a+b)=%d\n",a, b, time_after(a,a+b));
	

	a = ULONG_MAX; 
	b = ULONG_MAX/2+1;
	printf("a=%u,b=%u, time_after(a,a+b)=%d\n",a, b, time_after(a,a+b));

	a = ULONG_MAX; 
	b = ULONG_MAX/2+2;
	printf("a=%u,b=%u, time_after(a,a+b)=%d\n",a, b, time_after(a,a+b));

	a = ULONG_MAX/2; 
	b = 1;
	printf("a=%u,b=%u, time_after(a,a+b)=%d\n",a, b, time_after(a,a+b));
	

	a = ULONG_MAX/2; 
	b = ULONG_MAX/2;
	printf("a=%u,b=%u, time_after(a,a+b)=%d\n",a, b, time_after(a,a+b));
	
	a = ULONG_MAX/2+1;
	b = ULONG_MAX/2-1;
	printf("a=%u,b=%u, time_after(a,a+b)=%d\n",a, b, time_after(a,a+b));
	
	a = ULONG_MAX/2+1; 
	b = ULONG_MAX/2;
	printf("a=%u,b=%u, time_after(a,a+b)=%d\n",a, b, time_after(a,a+b));

	a = ULONG_MAX/2+1; 
	b = ULONG_MAX/2+1;
	printf("a=%u,b=%u, time_after(a,a+b)=%d\n",a, b, time_after(a,a+b));

}


 

程序结果:

root@linux:/home/gjt/test_demo# ./a.out

 char_range: [-2147483648, 2147483647]

uchar_range:[0   , 4294967295]

a=1,b=1, time_after(a,a+b)=0

a=4294967295,b=1, time_after(a,a+b)=0

a=4294967295,b=2147483647, time_after(a,a+b)=0

a=4294967295,b=2147483648, time_after(a,a+b)=1  //超过LONG_MAX ,不再适用

a=4294967295,b=2147483649, time_after(a,a+b)=1 //超过LONG_MAX ,不再适用

a=2147483647,b=1, time_after(a,a+b)=0

a=2147483647,b=2147483647, time_after(a,a+b)=0

a=2147483648,b=2147483646, time_after(a,a+b)=0

a=2147483648,b=2147483647, time_after(a,a+b)=0

a=2147483648,b=2147483648, time_after(a,a+b)=1 //超过LONG_MAX ,不再适用

root@linux:/home/gjt/test_demo# 

程序结果验证了这个宏的正确性。同样这个宏也有它的局限性,就是a,b两者的间隔时间一定要小于等于它们有符号数据类型的最大值,如unsigned long的最大值2147483647,超过这个值就不再适用。

一般情况下jiffies在32位CPU下最大无符号数是2^32,有符号数最大值是2^31。以jiffies的HZ为100来算,定时间隔超过2^31/100/3600/24=248天以上才会出问题,一般不会设置那么久的延时。所以这个宏可以放心使用。

另外再看个例子:

这个例子只是把上面程序的long 类型改成char类型,看一下结果会有什么不同。

#include <limits.h>
#include <stdio.h>

/*
* Check at compile time that something is of a particular type.
* Always evaluates to 1 so you may use it easily in comparisons.
*/
#define typecheck(type,x) \
({        type __dummy; \
        typeof(x) __dummy2; \
        (void)(&__dummy == &__dummy2); \
        1; \
})

#define time_after(a,b) \
(typecheck(unsigned char, a) && \
typecheck(unsigned char, b) && \
((char)(b) - (char)(a) < 0))

int main(int argc, char **argv)
{
	unsigned long a,b;

	printf("char_range: [%-4ld, %-4ld]\n",CHAR_MIN,CHAR_MAX);
	printf("uchar_range:[%-4lu, %-4lu]\n",0,UCHAR_MAX);
	
	a = 1; 
	b = 1;
	printf("a=%u,b=%u, time_after(a,a+b)=%d\n",a, b, time_after(a,a+b));
	

	
	a = UCHAR_MAX;
	b = 1;
	printf("a=%u,b=%u, time_after(a,a+b)=%d\n",a, b, time_after(a,a+b));

	a = UCHAR_MAX;
	b = UCHAR_MAX/2;
	printf("a=%u,b=%u, time_after(a,a+b)=%d\n",a, b, time_after(a,a+b));
	

	a = UCHAR_MAX; 
	b = UCHAR_MAX/2+1;
	printf("a=%u,b=%u, time_after(a,a+b)=%d\n",a, b, time_after(a,a+b));

	a = UCHAR_MAX; 
	b = UCHAR_MAX/2+2;
	printf("a=%u,b=%u, time_after(a,a+b)=%d\n",a, b, time_after(a,a+b));

	a = UCHAR_MAX/2; 
	b = 1;
	printf("a=%u,b=%u, time_after(a,a+b)=%d\n",a, b, time_after(a,a+b));
	

	a = UCHAR_MAX/2; 
	b = UCHAR_MAX/2;
	printf("a=%u,b=%u, time_after(a,a+b)=%d\n",a, b, time_after(a,a+b));
	
	a = UCHAR_MAX/2+1;
	b = UCHAR_MAX/2-1;
	printf("a=%u,b=%u, time_after(a,a+b)=%d\n",a, b, time_after(a,a+b));
	
	a = UCHAR_MAX/2+1; 
	b = UCHAR_MAX/2;
	printf("a=%u,b=%u, time_after(a,a+b)=%d\n",a, b, time_after(a,a+b));

	a = UCHAR_MAX/2+1; 
	b = UCHAR_MAX/2+1;
	printf("a=%u,b=%u, time_after(a,a+b)=%d\n",a, b, time_after(a,a+b));

}


程序结果:

root@linux:/home/gjt/test_demo# ./a.out 
char_range: [-128, 127 ]
uchar_range:[0   , 255 ]
a=1,b=1, time_after(a,a+b)=0
a=255,b=1, time_after(a,a+b)=0
a=255,b=127, time_after(a,a+b)=0
a=255,b=128, time_after(a,a+b)=0 //为何出错?
a=255,b=129, time_after(a,a+b)=1
a=127,b=1, time_after(a,a+b)=1 //为何出错?
a=127,b=127, time_after(a,a+b)=1 //为何出错?
a=128,b=126, time_after(a,a+b)=0
a=128,b=127, time_after(a,a+b)=0
a=128,b=128, time_after(a,a+b)=0 //为何出错?
root@linux:/home/gjt/test_demo# 

可以看出两个程序只是改了参数类型,结果却不同。这是因为我们编译器是32位的,计算char类型的溢出时编译器做了优化处理而导致结果不同。

这就是typecheck宏要类型检查的原因。确保参数传进来的就是long型。


本文同时参考了博客链接:

http://blog.csdn.net/jw212/article/details/6690802

http://blog.csdn.net/wshjldaxiong/article/details/8439518


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值