![c7345ea6bdb9214b27b579b22916eebc.png](https://i-blog.csdnimg.cn/blog_migrate/b7f3bf24dbc928f517b164ead0ce36c4.jpeg)
![804393a1f6f94bd3c39fea7f143d9aac.png](https://i-blog.csdnimg.cn/blog_migrate/dad1e1724d331ef39bd98638f3a2b62e.jpeg)
![12a117406552cb5096371e15f57b50be.png](https://i-blog.csdnimg.cn/blog_migrate/4c0025ea76bcd31ae32be12a59029b81.jpeg)
![07050c2a3bb76d2a4c8ddbf5f2f8bf46.png](https://i-blog.csdnimg.cn/blog_migrate/d56fb05a9ab73b08765226025b2d0b5b.jpeg)
![3e678497e0e7b74224769eca264dc602.png](https://i-blog.csdnimg.cn/blog_migrate/2c56ddb93751d962ec0f5fa94295003b.jpeg)
![25d21d35d1060bb7a0564e8a4e3a90be.png](https://i-blog.csdnimg.cn/blog_migrate/ecf3af6d3c2dc22d86ea9dd8dd7bf747.png)
![27a9ec143c34af9e0c8181171c8c9be6.png](https://i-blog.csdnimg.cn/blog_migrate/ed843480dd05cf93ed4cfae05cb857e1.jpeg)
同学们留言回复答案看看
可能很多老鸟对这样的Linux 内核宏已经见惯不怪了,但是作为新手的Linux内核开发者,我觉得非常有必要了解其中的原理和作用。
jiffies 这个想必大家已经非常熟悉,jiffies表示的是当前的系统时钟节拍总数,它统计的是从开机到现在的系统时间节拍。
既然说到时钟节拍,那就不能不说HZ,这个是系统的节拍,每个体系结构系统的节拍都不一样,内核中通常的节拍数都不同,是 100,200,1000等,根据不同的体系结构来设定,节拍数可以理解为心跳,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))
看注释,就是如果 a 的时间在 b 的时间之后,就返回true,也可以理解为产生b的时间段超时后,就返回true。
然后我们看看在内核代码里面是如何使用这个宏的?
timeout =2;
timeout += jiffies;
do {
if (time_after(jiffies, timeout)) {
/* drive timed-out */
return 1;
}
/* give drive a breather */
msleep(50);
} while ((hwif->INB(hd_status)) & BUSY_STAT);
我随便拿了一个代码来举例,这个是在驱动里面的一个代码,如果这个驱动代码产生了超时,就返回true,函数就返回,可以理解为注册驱动产生了超时时间后,while里面的判断还是真。
我们看这个宏实现的原理
如果
b = 100; (超时时间)
a = 55; (当前时间)
正常的时候
(long)b - (long)a > 0 表示没有产生超时
如果
a = 101时
(long)b - (long)a = 100 - 101 = -1 < 0 表示时间超时
但是我们正常不会这样使用,我们会利用HZ参数来一起使用
比如,我要设置2秒后超时,那么timeout可以这样设置
timeout = 2*HZ;
timeout += jiffies;
if(time_after(jiffies,timeout)){
//do somethings
}
但是前面有一个typecheck(unsigned long) 后面比较的时候又强制转变为long,这个有什么玄机呢?
这个主要是解决jiffies回绕的问题
我们知道unsigned long 的最大值是 2^64 -1 = 18446744073709551615 (64位系统)
![a0ff445116b93ba619df13cc4131efd9.png](https://i-blog.csdnimg.cn/blog_migrate/c4c26fc3278f662b83850877f2e384ee.png)
假设time_after的宏定义如下
#define time_after(a,b)
(typecheck(unsigned long, a) &&
typecheck(unsigned long, b) &&
((unsigned long)(b) - (unsigned long)(a) < 0))
//jiffies = 18446744073709550615
timeout = 2*HZ;
timeout += jiffies;
//do something
if(time_after(jiffies,timeout)){
//这时候,jiffies 已经回绕为 0,timeout还是一个很大的值,这时候就会出现问题了,jiffies需要重新计数很久很久才可能再回到和timeout比较的一个量级。
//do something
}
但是如果上面的宏,被强制转换成long 有符号数呢?
signed long 的范围是 [-9223372036854775808, 9223372036854775807]
#define time_after(a,b)
(typecheck(unsigned long, a) &&
typecheck(unsigned long, b) &&
((long)(b) - (long)(a) < 0))
jiffies = 18446744073709550615;
timeout =2*HZ;
timeout += jiffies;
//do something
if(time_after(jiffies,timeout)){
//这时候,jiffies 已经回绕为 0,timeout 还是一个很大 的值,转成有符号的long是 -801,这时候timeout - jiffies = -801 < 0是成立的。
//do something
}
我们看注释里面也写着,这个宏是非常强壮的,但是这个也有一个弊端的时候,就是timeout的时间超出了unsigned long /2 的范围,就会出现问题 ,但是unsigned long/2 表示多长的时间呢?我们计算一下
18446744073709551615 /HZ(200)/60/60/24 = 533759955836/2 = 266879977918(天)
没有谁把超时时间设置到这么久吧,所以说这个宏是足够你使用的了。
可能很多人不明白为什么timeout设置太长会出现问题,我们可以列举一下
#define time_after(a,b)
(typecheck(unsigned long, a) &&
typecheck(unsigned long, b) &&
((long)(b) - (long)(a) < 0))
jiffies = 18446744073709551615/2;
printf("%lldn",(long)(jiffies));
timeout = 18446744073709551615/2;
timeout += jiffies;
jiffies += 202;
printf("jiffies=%ld,timeout=%ld, time_after(a,a+b)=%dn",(long)jiffies, (long)timeout, time_after(jiffies,timeout));
//输出结果如下
9223372036854775807
jiffies=-9223372036854775607,timeout=-2, time_after(a,a+b)=0
请读者自行验证 jiffies =0 timeout = 18446744073709551615/2 的情况
看到最后返回的是 0 ,不是 1
原因很简单,因为timeout回绕变成了在 0附近的值(可以回去看那个图片加深理解),然后jiffies是一个负数很大的值,相减就出现问题了。
***************************
![35a3c46f4023b6ee4332b361e3af087b.png](https://i-blog.csdnimg.cn/blog_migrate/b388a8a026ada5a614b50741072b6e09.jpeg)