Где найти исходный код timespec_get?
C11
стандарт обеспечивает функцию timespec_get
, Если я запускаю пример кода на cppreference или на моем компьютере, он работает:
#include <stdio.h>
#include <time.h>
int main(void)
{
struct timespec ts;
timespec_get(&ts, TIME_UTC);
char buff[100];
strftime(buff, sizeof buff, "%D %T", gmtime(&ts.tv_sec));
printf("Current time: %s.%09ld UTC\n", buff, ts.tv_nsec);
}
Однако, если я посмотрю на источники glibc здесь, код будет следующим:
#include <time.h>
/* Set TS to calendar time based in time base BASE. */
int
timespec_get (struct timespec *ts, int base)
{
switch (base)
{
case TIME_UTC:
/* Not supported. */
return 0;
default:
return 0;
}
return base;
}
stub_warning (timespec_get)
Который... не должен работать...
Что приводит к вопросу: где находится исходный код timespec_get
что на самом деле называется?
2 ответа
timespec_get
Реализация функции зависит от системы, в которой работает библиотека, поэтому она выглядит как заглушка в time/timespec_get.c
(в случае, если реализация недоступна) и как различные зависящие от системы реализации в другом месте.
Вы можете увидеть реализацию Linux в sysdeps/unix/sysv/linux/timespec_get.c
,
/* Set TS to calendar time based in time base BASE. */
int
timespec_get (struct timespec *ts, int base)
{
switch (base)
{
int res;
INTERNAL_SYSCALL_DECL (err);
case TIME_UTC:
res = INTERNAL_VSYSCALL (clock_gettime, err, 2, CLOCK_REALTIME, ts);
if (INTERNAL_SYSCALL_ERROR_P (res, err))
return 0;
break;
default:
return 0;
}
return base;
}
Это всего лишь тонкая оболочка для вызова vDSO, и vDSO является частью самого ядра Linux. Если вам интересно, поищите определение clock_gettime
там. Необычно что clock_gettime
в vDSO, таким образом реализовано лишь небольшое количество системных вызовов.
Вот реализация x86 для CLOCK_REALTIME
, нашел в arch/x86/entry/vdso/vclock_gettime.c
:
/* Code size doesn't matter (vdso is 4k anyway) and this is faster. */
notrace static int __always_inline do_realtime(struct timespec *ts)
{
unsigned long seq;
u64 ns;
int mode;
do {
seq = gtod_read_begin(gtod);
mode = gtod->vclock_mode;
ts->tv_sec = gtod->wall_time_sec;
ns = gtod->wall_time_snsec;
ns += vgetsns(&mode);
ns >>= gtod->shift;
} while (unlikely(gtod_read_retry(gtod, seq)));
ts->tv_sec += __iter_div_u64_rem(ns, NSEC_PER_SEC, &ns);
ts->tv_nsec = ns;
return mode;
}
По сути, в вашем процессе есть некоторая память, которая обновляется ядром, и некоторые регистры в вашем процессоре, которые отслеживают ход времени (или что-то, предоставляемое вашим гипервизором). Память в вашем процессе используется для перевода значения этих регистров ЦП во время настенных часов. Вы должны прочитать их в цикле, потому что они могут измениться, пока вы их читаете... логика цикла обнаруживает случай, когда вы получаете неправильное чтение, и пытается снова.
timespec_get
определение, с которым вы связаны, является заглушкой (см. stub_warning
). Фактическая реализация будет под sysdeps
для вашей платформы. Например, вот версия для sysv
: https://github.com/lattera/glibc/blob/a2f34833b1042d5d8eeb263b4cf4caaea138c4ad/sysdeps/unix/sysv/linux/timespec_get.c
int timespec_get (ts, base)
struct timespec *ts;
int base;
{
switch (base)
{
int res;
INTERNAL_SYSCALL_DECL (err);
case TIME_UTC:
res = INTERNAL_GETTIME (CLOCK_REALTIME, ts);
if (INTERNAL_SYSCALL_ERROR_P (res, err))
return 0;
break;
default:
return 0;
}
return base;
}