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

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

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

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

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

То есть два значения были одинаковыми 33 миллиона раз, они были разными 177 тысяч раз, и они всегда отличались в пределах 5844 микросекунд от новой секунды.

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


person Josh Kelley    schedule 07.04.2014    source источник
comment
Я добавил пояснение к вашему вопросу, чтобы было понятнее, что вы получаете результаты, которые показывают, что время движется назад.   -  person Keith Thompson    schedule 07.04.2014
comment
Клиент ntp работает и меняет часы?   -  person Deduplicator    schedule 07.04.2014
comment
Я попробовал ваш код в реализации RedHat Linux и не нашел разных значений. Кажется, моя реализация vsyscall лучше.   -  person Sergey L.    schedule 07.04.2014
comment
@Deduplicator - я закрыл клиент NTP и все еще вижу это поведение.   -  person Josh Kelley    schedule 07.04.2014
comment
Интересно, это из-за того, что вы запускаете его в цикле, вы видите замедление внутри ядра.   -  person Engineer2021    schedule 07.04.2014
comment
Я вижу подобное поведение в своей системе Linux Mint x86_64. Версия вашей программы, которая выводит больше информации, показывает, что это указывает на то, что значение time() довольно последовательно обновляется между 7000 и 7003 микросекундами после обновления значения gettimeofday(). В моей системе Debian x86_64 (со старым ядром) несоответствий нет.   -  person Keith Thompson    schedule 07.04.2014
comment
Если я спрошу у вас время на ваших часах, а затем спрошу у вас время на настенных часах, они одинаковые? Иногда они есть, иногда нет. Итак, почему вы ожидаете, что два разных вызова будут равны по времени. Даже вызов time(NULL) дважды может дать разные результаты. Я помню, в старые дни DOS вызов функции времени рандомизировал дробную часть, чтобы сделать ее более «реальной». Если вам нужно, чтобы ваш хронометраж был последовательным, всегда одна и та же функция.   -  person Neil    schedule 15.05.2014
comment
@Neil: Дело не в том, что они неравны, а в том, что второй раз запрашивается раньше первого. Более подходящая аналогия: я спрашиваю у вас время в часах и минутах на ваших часах, затем я спрашиваю у вас время только в часах, и иногда вы даете мне пары ответов, например, 05:00, а затем 04.   -  person caf    schedule 06.06.2015


Ответы (3)


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

время():

использует функцию 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);
person akira    schedule 11.05.2014
comment
Это полезный ответ, но я чувствовал, что он на самом деле не объясняет, почему timekeeping_get_ns() может возвращать более миллиарда наносекунд. - person richvdh; 09.07.2015
comment
@richvdh bugs.php.net/bug.php?id=69044, благодаря NikiC - person Code4R7; 17.06.2019

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

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

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

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

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

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

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

t = time(NULL);
gettimeofday(&tv, NULL);
if (t > tv.tv_sec) { ...
person Sergey L.    schedule 07.04.2014
comment
Это, безусловно, объясняет то, что я вижу, но есть ли у вас какие-либо ссылки на документацию или исходный код ядра, подтверждающие это? Я сам пытался копаться в исходниках ядра, и мне не повезло. - person Josh Kelley; 07.04.2014
comment
порядок не имеет значения, смотрите мой ответ. обе функции также используют одну и ту же временную структуру в качестве базы, которая обновляется одновременно (поскольку это один и тот же экземпляр) для обеих функций. разница заключается в количестве накопленных наносекунд и в том, добавляются ли они к возвращенным секундам. - person akira; 12.05.2014

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

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

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

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

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

person richvdh    schedule 09.07.2015