21

私がテストした 2 つのシステム (32 ビット Ubuntu 12.04 サーバーと 64 ビット Ubuntu 13.10 VM) では、time()によって指定されたエポックからの秒数がgettimeofday()のものと異なる場合があります。

具体的には、 を呼び出しtime() た後に呼び出すgettimeofday()のですが、 の戻り値が の戻り値よりtime()小さい場合があります。tv_secgettimeofday()

これは、クロックが新しい秒にロールオーバーした直後に発生するようです。

これにより、私のコードの一部で、time() と gettimeofday() の秒が交換可能であると予想されるバグが発生しました。

この問題を示すサンプル コード:

#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);
}

time() second を呼び出しており、その値がgettimeofday() の値よりも小さい場合にのみ文句を言うことに注意してください。

出力例:

Same:      33282836
Different: 177076
Largest difference seen at 5844

つまり、2 つの値は 3300 万回同じで、177,000 回異なっており、新しい秒の 5844 マイクロ秒以内で常に異なっていました。

これは既知の問題ですか? これは何が原因ですか?

4

3 に答える 3

22

両方の呼び出しは、カーネル syscalls として実装されます。どちらの関数も最終的に a を読み取りstruct timekeeper、どちらもまったく同じインスタンスを参照します。しかし、彼らはそれで何をするかが異なります:

時間() :

get_seconds()これへのショートカットである関数を使用します。

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

返すだけxktime_secです。

gettimeofday():

gettimeofday()一方、両方のフィールドを読み取る (via ) とdo_gettimeofday()(via )を使用します。ここでは、1 秒よりも多くのナノ秒を保持することが発生する可能性があります。この潜在的な余分な時間は、これを行う関数を呼び出してフィールドを増やすために使用されます。getnstimeofdayxktime_secxktime_nsectimekeeping_get_nsxktime_nsectv_sectimespec_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()

今日、フラックスボックスでこの問題と闘いましたが、より良い解決策が得られるまで、私はこれと一緒に暮らしています:

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

timeとは両方とも、gettimeofdayいわゆる Linux vsyscalls として実装されています。コードは、カーネルが所有するが、定期的にのみ更新される結果を含むユーザースペースにマップされたページにリダイレクトされることを意味します。

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 に答える