最近受到内核时钟精度的困扰。 具体情况如下:
盲: 1秒=1000毫秒=1000000微妙=1000000000纳秒
首先,linux有一个重要概念,即——节拍,单位为(次/秒)。 2.6内核这个值为1000,在系统中这个值用HZ的宏表示。 同时,有一个全局jiffies变量,表示打开电源后经过的节拍次数。 (这其中也有故事,稍后再解释。 请先记住这个。 当然wall_jiffies的墙上有jiffies,表示从07-01-1970到现在的节奏数。 每个节拍执行一次时钟中断。 也就是说,其精度是毫秒。
接下来,内核有另一个变量xtime,其表示系统的实际时间(墙壁时间),定义如下: 其中,xtime.tv_sec以秒为单位,存储从Unix的祖先决定的纪元时间(19700701 )到现在的秒数。 xtime.tv_nsec以纳秒为单位记录自上一秒起经过的纳秒数。 也就是说,是纳秒的精度。
C
struct timespec xtime;
struct timespec{
time_t tv_sec; //秒
long tv_nsec; //纳秒
(;
1
2
3
4
5
6
structtimespecxtime;
structtimespec{
time_ttv_sec; //秒
longtv_nsec; //纳秒
(;
最后,linux提供gettimeofday的系统调用。 它返回timeval的结构,定义如下: 如上所述,tv_sec表示墙壁的时间秒,tv_usec表示从上一秒到当前的经过微秒数。 也就是说,其精度很微妙。
C
struct timeval{
long tv_sec; //秒
long tv_usec; //微妙
(;
1
2
3
4
structtimeval{
longtv_sec; //秒
longtv_usec; //微妙
(;
来得太棒了:
1 .每次时钟中断时,内核中的xtime都会更新。 也就是说,每次节拍都会更新。 你妹妹! 每毫秒更新会产生纳秒的精度吗? 另外,内核也有可能失去节奏。 纳秒是怎么做的?
2 .根据各种书籍,gettimeofday系统调用是读取的xtime的值。 今天,为什么读了之后失去了精度? 变得微妙了吗?
我找了一下,故事终于整理好了:
问题1 ) linux启动时,1节拍的时间长度以纳秒为单位初始化为tick_nsec。 初始化值为999848ns。 坑爹啊。 不到一毫秒! 节奏约为1000.15Hz。 靠过来! 实际的节奏不是正确的1000! 因此,每个时钟中断在wall_jiffies中更新xtime时获得的是以纳秒为最小单位的值。 所以! xtime的粒度应该小于1毫秒,也就是说精度应该小于1毫秒。
2:gettimeday系统调用的xtime代码读取部分如下。
C
do{
未标记的长整型;
seq=read_seqbegin(xtime_lock );
usec=timer-get_offset (; //在计时器上取从上次时钟中断到现在的微秒数
lost=jiffies - wall_jiffies;
是if (迷失)
usec=lost*(1000000/Hz; //HZ是节拍宏,值为1000
sec=xtime.tv_sec;
usec=(xtime.TV_nsec/1000 ); //由纳秒变成微妙
}while(read_seqretry ) xtime_lock,seq ) )
1
2
3
4
5
6
7
8
9
10
11
12
do{
未标记的长整型;
seq=read_seqbegin(xtime_lock );
usec=timer-get_offset (; //在计时器上取从上次时钟中断到现在的微秒数
lost=jiffies-wall_jiffies;
是if (迷失)
usec=lost*(1000000/Hz; //HZ是节拍宏,值为1000
sec=xtime.tv_sec;
usec=(xtime.TV_nsec/1000 ); //由纳秒变成微妙
}while(read_seqretry ) xtime_lock,seq ) )
while部分使用了seg锁。 只看中间的就好了。 加注释就清楚了。 由于节奏可能丢失,lost是丢失的节奏数。 虽然计时器很麻烦,但timer有以下四种情况。
如果cur_timer指向timer_HPET对象,则此方法至少使用hpet计时器——Inter和Microsoft开发的高精度计时器频率10MHz。 也就是说,此时可以提供真正微妙的精度水平。
当cur_timer指向timer_pmtmr对象时,该方法还可以通过使用ACPI PMT定时器(电源管理定时器)的平均速率约为3.58MHz来提供真正微妙级别的精度。
如果cur_timer指向timer_tsc对象,则该方法使用时间戳计数器,内置于所有8086处理器中,每个CPU时钟增加一次计数器,频率为CPU频率,因此timer的精度最高足以承受微妙水平的精度。
如果cur_timer指向timer_PIT对象,则此方法使用pit计数器,即第一个节拍计数。 频率约为1000Hz,此时无法提供精度明显微妙的时间。 所以只有在这种情况下是假毫秒精度!
总的来说,使用gettimeofday系统调用的话,只要不使用速度计数器就可以确保达到微妙精度的时间(削减过程上下文的时间误差)。 网络上说的关于能得到纳秒精度的时间,看起来都是错误的。 除非更改内核,否则使用时间戳计数器实现。 太棒了!
最后说一件事。 jiffies的定义是4个字节。 你可能觉得初始值是0。 其实,不是那样的! 在linux中,jiffies初始化为0xfffb6c20,是32位的有符号数,正好等于-300 000。 因此,计数器将在系统启动后的5分钟内溢出。 这是为了在开发阶段发现jiffies溢出处理有缺陷的内核代码,从而避免在稳定的版本中出现这样的问题。
参照《深入理解linux内核》
原文链接