如何计算时间间隔(2014/2/20)

在进行重要的时间运算的时候,比如自己实现定时器,不能够使用time/gettimeofday,建议使用TSC或jiffies。

1 问题

1.1现象1

应用(ePDG)中有许多的定时器,这些定时器通过一个队列和一个线程进行管理。定时器加入到队列时,用time/gettimeofday获取当前时间(记为timer_start),加上用户传入的延时参数(delay)会,得到定时器应该被触发的时间(timer_start+delay)。线程每隔10ms,用time/gettimeofday获取当前时间(curr_time),检查队列上的定时器的触发时间是否小于当前时间,即满足如下条件的定时器应该被触发

(timer_start+delay)<curr_time

在系统运行过程中,用date命令修改OS时间后,发现定时器不会在指定的时间内被触发。

1.2 现象2

应用(ePDG)会计算一个用户在线时长。计算用户在线时长非常简单,用户上线时调用time()函数获取当前时间(logon_time),用户下线是调用time()函数获取当前时间(logout_time),logout_time-logon_time即用户的在线时长。

用户上线以后,用date命令修改OS时间后,发现用户的在线时长明显与实际不符。

2 分析

由于time/gettimeofday函数获取的是OS wall时间,即我们看到的系统时间。如果修改wall 时间,time/gettimeofday函数获取到的时间是会随之变化的。所以,在不同的时间点调用time/gettimeofday函数获得当前时间,然后相减获得时间间隔的方法是不对的。

3 解决方案

使用TSC或jiffies来计算时间间隔。

4 关于时间

4.1 实际时间/wall时间

实际时间(墙上时间)定义在文件kernel/timer.c中:

struct timespec xtime;

timespec数据结构定义在文件<linux/time.h>中,形式如下:

structtimespec{      
    time_t  tv_sec;           /* 秒 */
    longtv_nsec;               /* 纳秒 */
};

其中,xtime.tv_sec以秒为单位,存放着自1970年7月1日以来经过的时间。xtime.tv_nsec记录了自上一秒开始经过的纳秒数。xtime是用过time interrupt来更新的。从用户空间取得墙上时间的主要接口是gettimeofday/time。

实时时钟(RTC)是用来持久存放系统时间的设备,即便系统关闭后,它可以靠主板上的微型电池提供的电力保持系统的计时。系统启动时,内核通过读取RTC来初始化WallTime,并存放在xtime变量中,这是RTC最主要的作用我们可以通过date命令修改实时时钟。

4.2 jiffies

HZ

Linux核心每隔固定周期会发出timerinterrupt (IRQ 0),即每隔固定时间间隔调用一次时间中断。HZ是用来定义每一秒有几次timer interrupts。可以通过/proc/HZ查看HZ的值。

TICK

TickHZ的倒数,意即timerinterrupt每发生一次中断的时间。如HZ250时,tick4毫秒(millisecond)

jiffies

<linux/jiffies.h>,定义了JiffiesLinux核心变数(32位元变数,unsignedlong),它被用来纪录系统自开机以来,已经过多少的tick,在linux内核中jiffies远比xtime重要。每发生一次timer interruptJiffies变数会被加一。由于时间中断是通过底层的硬件实现的,所以通过jiffies获取时间间隔不会随着wall时间改变而改变。

4.3 TSC

TSC(time stamp counter)记录自启动以来处理器消耗的时钟周期数,它是intel CPU提供的一个计数器。它在每个时钟周期到来时,该计数器自动加一。因为 TSC 随着处理器周期速率的变化而变化,所以它提供了非常高的精确度。它经常被用来分析和检测代码。TSC 的值可以通过 rdtsc 指令来读取。TSC 的节拍还可以转换为秒,转换方法是将其除以 CPU 的时钟速率(可以从内核变量 cpu_khz 获取)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章