example - gettimeofday c++
time() y gettimeofday() devuelven segundos diferentes (3)
En los dos sistemas que he probado (un servidor Ubuntu 12.04 de 32 bits y una máquina virtual 13.10 de Ubuntu de 64 bits), los segundos desde la época dada por time() pueden diferir de los de gettimeofday() .
Específicamente, aunque llame a time()
después de llamar a gettimeofday()
, el valor devuelto por time()
veces es menor que el valor de tv_sec
devuelto por gettimeofday()
.
Aparentemente, esto ocurre justo después de que el reloj se acerca a un nuevo segundo.
Esto causó errores en algunos de mis códigos que esperaban que los segundos de time () y gettimeofday () fueran intercambiables.
Código de muestra que demuestra este problema:
#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);
}
Tenga en cuenta que estoy llamando a time () en segundo lugar y solo me quejo si su valor es menor que gettimeofday ().
Salida de muestra:
Same: 33282836
Different: 177076
Largest difference seen at 5844
Es decir, los dos valores fueron los mismos 33 millones de veces, fueron diferentes 177k veces y siempre fueron diferentes en 5844 microsegundos de un segundo nuevo.
¿Es este un problema conocido? ¿Qué causa esto?
Ambas llamadas se implementan como syscalls del kernel. Ambas funciones terminan leyendo una struct timekeeper
, ambas se refieren a la misma instancia. Pero difieren en lo que hacen con él:
time() :
utiliza la función get_seconds()
, que es un acceso directo a esto:
struct timekeeper *tk = &timekeeper;
return tk->xtime_sec;
simplemente devuelve xktime_sec
.
gettimeofday()
:
gettimeofday()
otra parte, do_gettimeofday()
utiliza do_gettimeofday()
(a través de getnstimeofday
) que lee los campos xktime_sec
y xktime_nsec
(a través de timekeeping_get_ns
). Aquí puede suceder que xktime_nsec
tenga más nanosegundos que un segundo. Este tiempo adicional potencial se usa para aumentar el campo tv_sec
llamando a la función timespec_add_ns()
que hace esto:
a->tv_sec += __iter_div_u64_rem(a->tv_nsec + ns, NSEC_PER_SEC, &ns);
a->tv_nsec = ns;
Entonces, tv_sec
podría ser más grande que el campo xktime_sec
. Y ahí lo tienen: una pequeña diferencia en qué time()
le da y qué le da gettimeofday()
.
Luché contra este problema en Fluxbox hoy y hasta que se presente una solución mejor, vivo con esto:
uint64_t t_usec = gettimeofday_in_usecs(); // calcs usecs since epoch
time_t t = static_cast<time_t>(t_usec / 1000000L);
Este comportamiento se debe a la implementación del cronometraje en el kernel de Linux.
Linux mantiene una variable que rastrea la hora actual del reloj de pared; Esto se mantiene a nanosegundos de precisión y se actualiza periódicamente. (Es tk_core.timekeeper.{xtime_secs, tkr_mono.xtime_nsec}
en versiones recientes del kernel).
time()
llama a get_seconds() que simplemente devuelve la parte de los segundos de esta variable, por lo que, dependiendo de cuánto tiempo se actualizó la hora del reloj de pared, puede devolver un valor ligeramente desactualizado.
gettimeofday()
no solo lee el último valor de las variables de reloj de pared, sino que también (a través de timekeeping_get_ns() ) realiza una nueva lectura del reloj de hardware (normalmente el TSC en sistemas x86, aunque esto es configurable en tiempo de ejecución) y aplica una corrección.
Gracias a ese cálculo de corrección, es posible que el resultado devuelto por gettimeofday()
tv_sec
al siguiente segundo y, por lo tanto, devuelva un valor de tv_sec
más alto que el resultado de time()
.
Tanto time
como gettimeofday
se implementan como los llamados Linux vsyscalls. Significa que su código será redirigido al núcleo del kernel, pero las páginas mapeadas en el espacio de usuario que contienen los resultados que solo se actualizan periódicamente.
En Ubuntu (no he observado este comportamiento en RedHat Linux) el valor de gettimeofday
se actualiza antes que el valor de time
por lo que es posible obtener valores inconsistentes:
actualizaciones del kernel
gettimeofday
gettimeofday
Preguntas
time
el kernel actualiza el
time
El intercambio de sus llamadas da resultados consistentes:
t = time(NULL);
gettimeofday(&tv, NULL);
if (t > tv.tv_sec) { ...