Подтвердить что ты не робот

Time() и gettimeofday() возвращают разные секунды

В двух системах, которые я тестировал (32-разрядный сервер Ubuntu 12.04 и 64-разрядная Ubuntu 13.10 VM), секунды с момента, заданного time() может отличаться от gettimeofday().

В частности, хотя я вызываю time() после вызова gettimeofday(), значение, возвращаемое time(), иногда меньше значения tv_sec, возвращаемого gettimeofday().

Это, по-видимому, происходит сразу после того, как часы переходят к новой секунде.

Это вызвало ошибки в некоторых из моего кода, в которых ожидалось, что время() и 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() второй и только жалуюсь, если его значение меньше, чем gettimeofday().

Пример вывода:

Same:      33282836
Different: 177076
Largest difference seen at 5844

I.e., эти два значения были одинаковыми 33 миллиона раз, они были разными 177k раз, и они всегда отличались в течение 5844 микросекунд новой секунды.

Это известная проблема? Что вызывает это?

4b9b3361

Ответ 1

Оба вызова реализованы как системные вызовы ядра. Обе функции заканчивают чтение struct timekeeper, оба относятся к одному и тому же экземпляру. Но они отличаются тем, что с ним делают:

time():

использует функцию get_seconds(), которая является ярлыком для этого:

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

он просто возвращает xktime_sec.

gettimeofday():

gettimeofday() использует do_gettimeofday() (через getnstimeofday), который читает оба поля xktime_sec, а также xktime_nsec (через 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);

Ответ 2

Оба time и gettimeofday реализованы как так называемые Linux vsyscalls. Означает, что ваш код будет перенаправлен на принадлежащее ядру, но сопоставленные страницы в пользовательском пространстве, содержащие результаты, которые периодически обновляются.

В Ubuntu (я не наблюдал этого поведения в RedHat Linux) значение для gettimeofday обновляется до значения для time, поэтому можно получить несогласованные значения:

Обновление ядра gettimeofday

Вы запрашиваете gettimeofday

Вы запрашиваете time

Обновления ядра

time

Обмен вашими вызовами дает согласованные результаты:

t = time(NULL);
gettimeofday(&tv, NULL);
if (t > tv.tv_sec) { ...

Ответ 3

Такое поведение связано с внедрением хронометража в ядре Linux.

Linux поддерживает переменную, которая отслеживает текущее время настенных часов; это поддерживается до наносекундной точности и периодически обновляется. (В последних версиях ядра это tk_core.timekeeper.{xtime_secs, tkr_mono.xtime_nsec}.)

time() вызывает get_seconds(), который просто возвращает секундную часть этой переменной - так, в зависимости от того, как давно настенные часы время было обновлено, может вернуть немного устаревшее значение.

gettimeofday() не только считывает последнее значение переменных настенных часов, но также (через timekeeping_get_ns()) делает новое чтение от аппаратных часов (обычно TSC в системах x86, хотя это настраивается во время выполнения) и применяет исправление.

Благодаря этому вычислению коррекции можно вернуть результат, возвращаемый gettimeofday() в следующую секунду, и, следовательно, вернуть значение tv_sec выше результата time().