在哪里可以找到timespec_get的源代码?

时间:2018-07-24 02:41:51

标签: c linux glibc c11 timespec

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 here的源代码,则代码如下:

#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 个答案:

答案 0 :(得分:4)

timespec_get函数的实现取决于运行库的系统,因此它在time/timespec_get.c中显示为存根(如果没有可用的实现),并且在其他地方也表现为各种与系统相关的实现

您可以在sysdeps/unix/sysv/linux/timespec_get.c

中看到Linux的实现。
/* 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中是不寻常的,通过这种方式只能实现少量的系统调用。

这是CLOCK_REALTIME中的arch/x86/entry/vdso/vclock_gettime.c的x86实现:

/* 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;
}

基本上,您的进程中有一些内存会由内核更新,而CPU中的一些寄存器会跟踪时间的流逝(或虚拟机管理程序提供的某些内存)。您进程中的内存用于将这些CPU寄存器的值转换为挂钟时间。您必须循环阅读这些内容,因为它们在读取过程中可能会发生变化...循环逻辑会在您读取错误内容时检测到这种情况,然后重试。

答案 1 :(得分:1)

您链接到的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;
}