曲径通幽论坛

 找回密码
 立即注册
搜索
查看: 10779|回复: 0
打印 上一主题 下一主题

time_after | time_before | time_after_eq | time_before_eq

[复制链接]

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34395
跳转到指定楼层
楼主
发表于 2011-1-15 10:32:52 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
平台:x86
内核:2.6.24
time_after() , time_before() , time_after_eq() , time_before_eq() 其实是 4 个和时间比较相关的宏,定义在 include/linux/jiffies.h 文件中。

定义代码如下:
[C++] 纯文本查看 复制代码
#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)


这 4 个宏对时间的比较是和 jiffies 相关的比较。在宏中,参数 a 是 jiffies 在某个时刻的快照。在 time_after(a,b) 里,如果 a 所代表的时间比 b 靠后,那么返回真。如果 a 比 b 靠前,则 time_before(a,b) 返回真;而 time_after_eq(a,b) 和 time_before(a,b) 则分别比较 “靠后或者相等” 及 “靠前或者相等”。

另外,在这 4 个宏中,实际上都会用到 typecheck() 这个宏进行类型的检查。关于 typecheck() 的分析可参考:http://www.groad.net/bbs/read.php?tid-3066.html


为什么要使用上面的宏,而不是直接比较时间?
下面这段代码会因为 jiffies 的溢出而导致逻辑错误:
[C++] 纯文本查看 复制代码
unsigned long timeout; 

init_device();   // 初始化设备 

timeout = jiffies + HZ; 

while (!device_is_ok()) {   //如果设备还没准备好且没有超时就在此等待,如果超时则退出 
      if (jiffies > timeout) { 
            timeout() 
            exit; 
      } 
} 
other_works();

在 init_device(); 初始化设备后,设定在 1 秒钟超时时间 timeout 。如果 1 秒钟后设备还不能准备就绪( device_is_ok() == 1),那么进行超时处理,然后退出;如果在此期间设备准备好,那么就进行其它的工作 (other_works()) 。

上面的程序逻辑乍看上去没问题,但是如果考虑到 jiffies 的溢出,就会发现导致逻辑出错的可能。现在假设 jiffies 的值处于 (2^32 - 1 - 999) ~ (2^32 - 1) 这个范围中,同时 HZ 假设取值 1000 ,那么 timeout 的取值必定在 (0 ~ 999)之间。因为外设的响应速度比较慢,第一次通过 device_is_ok() 函数来判断设备是否就绪往往不会成功(函数返回 0),那么接下来就会再判断时间是否超时。也就是这时候,我们可以假设有这样一种情况,比如程序在进行 jiffies + HZ 计算时 jiffies 处于 2^32 - 5 这里,那么在加上 1000 后,则 timeout 的值则为 995 (发生了回绕) 。那么在接下来的比较中,jiffies 必定大于 timeout ,也就是说才执行第一次判断就发生了超时,这样显然是个错误。

而如果使用了 time_after() 这样的宏,它就不会出现这种错误。time_after() 宏(其它的类似),首先确保两个输入参数 a 和 b 为无符号长整型,然后再强制转换为又符号的长整型再进行比较。在又符号的长整型中,是存在负数的,比如 0xfffffffe 是 -2 而不是 4294967294 。所以,像上面的那种假设,我们是用 995 和 -2 在比较,故而不会有溢出的错误了。

需要注意的是,对于 32 位无符号整型,两个值之间的相差从逻辑上来讲应该小于 2,147,483,647 (0x7fffffff)。这是因为在将无符号整型转换为有符号整型后,到了 0x80000000 就会出现负数的情况,所以这时候再比较也会比较错误。也就是说,对于 HZ=1000 ,两个用于比较的 jiffies 所代表的时间值之差不应当超过 2,147,483,647/1000 秒,即 24.85 天。在实际的应用中,需要比较先/后的两个时间值之间一般都相差很小,范围大致在 1秒 -- 1天 左右,故用上面的宏可以成功的解决因为 32 位 jiffies 变量因回绕而导致逻辑错误的问题。


示例代码:
[C++] 纯文本查看 复制代码
#include <linux/config.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>

#include <linux/time.h>
#include <linux/timer.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>

#include <asm/hardirq.h>

int delay = HZ;

int jit_busy(char *buf, char **start, off_t offset, int len, int *eof, void *data)
{
        unsigned long j0, j1;
        j0 = jiffies;
        j1 = j0 + delay;

        while (time_before(jiffies, j1)) {
                cpu_relax();
        }
        len = sprintf (buf, "%9li %9li\n", j0, j1);
        *start = buf;

        return len;
}
int __init jit_init(void)
{
        create_proc_read_entry ("jitbusy", 0, NULL, jit_busy, NULL);
        return 0;
}
void __exit jit_cleanup(void)
{
        remove_proc_entry("currentime", NULL);
}
module_init (jit_init);
module_exit (jit_cleanup);

运行输入:
[root@centos jit]# dd bs=20 count=5 < /proc/jitbusy
  8137857   8138107
  8138108   8138358
  8138358   8138608
  8138608   8138858
  8138858   8139108
5+0 records in
5+0 records out
100 bytes (100 B) copied, 5.0023 seconds, 0.0 kB/s
上面使用 time_before() 作忙等待,每 1 秒钟进行一次输出。dd 命令读取 /proc/jitbusy 文件时,每次读取 20 个字节数据,总共读取 5 次,也就是说整个读取过程经历 5 秒。从输出可以看出,每次输出的刚好间隔 1S 。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|曲径通幽 ( 琼ICP备11001422号-1|公安备案:46900502000207 )

GMT+8, 2025-5-4 01:05 , Processed in 0.075965 second(s), 23 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

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