time() и gettimeofday() връщат различни секунди

На двете системи, които тествах (32-битов Ubuntu 12.04 сървър и 64-битов Ubuntu 13.10 VM), секундите от епохата са дадени от 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() секунда и се оплаквам само ако стойността му е по-малка от тази на gettimeofday().

Примерен резултат:

Same:      33282836
Different: 177076
Largest difference seen at 5844

Т.е., двете стойности са били еднакви 33 милиона пъти, те са били различни 177k пъти и винаги са били различни в рамките на 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(). В моята система x86_64 Debian (работеща с по-старо ядро) не показва несъответствия.   -  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>, и двете се отнасят за един и същ екземпляр. Но те се различават по това какво правят с него:

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);
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 vsyscalls. Означава, че вашият код ще бъде пренасочен към страници, притежавани от ядрото, но картографирани в потребителско пространство, съдържащи резултатите, които се актуализират само периодично.

В 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
comment
Благодаря! Цитирам публикацията ви Отпечатване на текущото време в милисекунди или наносекунди с вграден printf - person F. Hauri; 14.04.2020