21

On the two systems I've tested (a 32-bit Ubuntu 12.04 server and a 64-bit Ubuntu 13.10 VM), the seconds since the epoch given by time() may differ from gettimeofday()'s.

Specifically, though I call time() after calling gettimeofday(), the value returned by time() is sometimes less than the tv_sec value returned by gettimeofday().

This apparently occurs just after the clock rolls over to a new second.

This caused bugs in some of my code that expected time()'s and gettimeofday()'s seconds to be interchangeable.

Sample code demonstrating this problem:

#include <stdio.h>
#include <time.h>
#include <sys/time.h>

int main()
{
  time_t start = time(NULL);
  int same = 0;
  int different = 0;
  int max_usec = 0;
  while (1) {
    time_t t;
    struct timeval tv;
    gettimeofday(&tv, NULL);
    t = time(NULL);
    if (t < tv.tv_sec) {
      different++;
      if (tv.tv_usec > max_usec) {
        max_usec = tv.tv_usec;
      }
    } else {
      same++;
    }
    if (t > start + 5) {
      break;
    }
  }
  printf("Same:      %i\n", same);
  printf("Different: %i\n", different);
  printf("Largest difference seen at %i\n", max_usec);
}

Note that I'm calling time() second and only complaining if its value is less than gettimeofday()'s.

Sample output:

Same:      33282836
Different: 177076
Largest difference seen at 5844

I.e., the two values were the same 33 million times, they were different 177k times, and they were always different within 5844 microseconds of a new second.

Is this a known issue? What causes this?

4

3 回答 3

22

这两个调用都是作为内核系统调用实现的。两个函数最终都读取 a struct timekeeper,都引用同一个实例。但它们的处理方式不同:

时间()

使用该get_seconds()函数,这是一个快捷方式:

struct timekeeper *tk = &timekeeper;
return tk->xtime_sec;

它只是返回xktime_sec

gettimeofday()

gettimeofday()另一方面,使用do_gettimeofday()(via getnstimeofday) 读取两个字段xktime_sec以及xktime_nsec(via timekeeping_get_ns)。在这里,可能会发生xktime_nsec比一秒更多的纳秒。这个潜在的额外时间用于tv_sec通过调用timespec_add_ns()执行此操作的函数来增加字段:

a->tv_sec += __iter_div_u64_rem(a->tv_nsec + ns, NSEC_PER_SEC, &ns);
a->tv_nsec = ns;

因此,tv_sec可能会比该xktime_sec领域更大。你有它:time()给你的东西和gettimeofday()给你的东西有一点不同。

我今天在fluxbox中与这个问题作斗争,直到出现更好的解决方案,我接受了这个:

uint64_t t_usec = gettimeofday_in_usecs(); // calcs usecs since epoch
time_t t = static_cast<time_t>(t_usec / 1000000L);
于 2014-05-11T20:44:44.993 回答
9

两者timegettimeofday都是作为所谓的 Linux vsyscall 实现的。意味着您的代码将被重定向到内核所有,但用户空间映射的页面包含仅定期更新的结果。

在 Ubuntu 中(我在 RedHat Linux 中没有观察到这种行为)的值在值gettimeofday之前更新,time因此可能会得到不一致的值:

内核更新gettimeofday

你查询gettimeofday

你查询time

内核更新time

交换你的电话会产生一致的结果:

t = time(NULL);
gettimeofday(&tv, NULL);
if (t > tv.tv_sec) { ...
于 2014-04-07T16:27:44.610 回答
7

这种行为是由于在 Linux 内核中实现了计时。

Linux 维护一个跟踪当前挂钟时间的变量;这保持在纳秒精度并定期更新。(它tk_core.timekeeper.{xtime_secs, tkr_mono.xtime_nsec}在最近的内核版本中。)

time()调用get_seconds(),它只返回此变量的秒部分 - 因此,根据挂钟时间更新的时间长短,可能会返回稍微过时的值。

gettimeofday()不仅读取挂钟变量的最新值,而且(通过timekeeping_get_ns())从硬件时钟(通常是 x86 系统中的TSC,尽管这可以在运行时配置)进行新的读取并应用修正。

由于该校正计算,返回的结果可能gettimeofday()会滚动到下一秒,因此返回的tv_sec值高于 的结果time()

于 2015-07-09T10:41:05.103 回答