【问题标题】:How to check if the system supports "Monotonic Clock"?如何检查系统是否支持“单调时钟”?
【发布时间】:2018-05-09 04:09:14
【问题描述】:

我需要在代码中处理超时场景,如果系统支持单调时钟,我想使用clock_gettime(CLOCK_MONOTONIC)

#ifdef CLOCK_MONOTONIC
    clock_gettime(CLOCK_MONOTONIC, & spec);
#else
    clock_gettime(CLOCK_REALTIME,  & spec);
#endif

我不确定这是否足够。也就是说,系统定义的CLOCK_MONOTONIC 是不是真的不支持单调时钟?或者检查是否支持单调时钟的可靠方法是什么?

【问题讨论】:

  • 不符合 POSIX/SUS。
  • if (clock_gettime (CLOCK_MONOTONIC, &time) == -1) perror ("clock_gettime"); ??

标签: c linux unix posix


【解决方案1】:

根据 POSIX 的规定,即使定义了常量 CLOCK_MONOTONIC,您实际上也可能需要运行时测试。处理这个问题的官方方法是使用_POSIX_MONOTONIC_CLOCK“功能测试宏”,但这些宏的语义非常复杂:引用http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/unistd.h.html

如果符号常量未定义或定义为值 -1,则编译不支持该选项。如果定义的值大于零,则在执行应用程序时应始终支持该选项。如果它被定义为零值,则该选项应支持编译,并且在运行时可能支持也可能不支持。

将这三方面的区别转化为代码会给你这样的结果:

#if !defined _POSIX_MONOTONIC_CLOCK || _POSIX_MONOTONIC_CLOCK < 0
    clock_gettime(CLOCK_REALTIME, &spec);
#elif _POSIX_MONOTONIC_CLOCK > 0
    clock_gettime(CLOCK_MONOTONIC, &spec);
#else
    if (clock_gettime(CLOCK_MONOTONIC, &spec))
        clock_gettime(CLOCK_REALTIME, &spec));
#endif

但是如果你总是在定义 CLOCK_MONOTONIC 本身时进行运行时测试,它会更简单,更易读:

#ifdef CLOCK_MONOTONIC
    if (clock_gettime(CLOCK_MONOTONIC, &spec))
#endif
        clock_gettime(CLOCK_REALTIME, &spec);

这在支持CLOCK_MONOTONIC 的当前一代操作系统上增加了一些微不足道的代码大小,但在我看来,可读性的好处是值得的。

对于无条件使用CLOCK_MONOTONIC 也有很强的论据;与具有 clock_gettime 但不支持 CLOCK_MONOTONIC 的操作系统相比,您更有可能找到根本不支持 clock_gettime 的操作系统(例如,据我所知,MacOS X 仍然没有它)。

【讨论】:

  • “不过,就我个人而言,我只会无条件地使用 CLOCK_MONOTONIC,除非有人抱怨。”,不实现它的东西不值得我们的程序 ;)
  • 查看我的答案以在 macOS 上查看有趣的结果。
【解决方案2】:

POSIX 只要求存在CLOCK_REALTIME,其他时钟是可选的。

如果单调时钟可用,宏 _POSIX_MONOTONIC_CLOCK 将在 unistd.h 中定义(根据 man page可用性 部分)

【讨论】:

  • 所有_POSIX_ 宏都有非常复杂的语义。引用pubs.opengroup.org/onlinepubs/9699919799/basedefs/unistd.h.html:“如果符号常量未定义或定义为值-1,则不支持编译该选项。如果定义的值大于零,则应用程序时应始终支持该选项被执行。如果它被定义为零值,则该选项应支持编译,并且在运行时可能支持也可能不支持。所以你必须做一个三路#if,你可能还需要一个运行时测试。
【解决方案3】:

如@zwol 的回答所示,事情确实变得有点复杂和有趣--

请参阅以下简单程序 (foo.c):

#include <stdio.h>
#include <unistd.h>
#include <time.h>

int
main()
{
    struct timespec spec;

    printf("_POSIX_MONOTONIC_CLOCK         = %d\n",
           (int)_POSIX_MONOTONIC_CLOCK);
    printf("sysconf(_SC_MONOTONIC_CLOCK)   = %ld\n",
           sysconf(_SC_MONOTONIC_CLOCK) );
    printf("clock_gettime(CLOCK_MONOTONIC) = %d\n",
           clock_gettime(CLOCK_MONOTONIC, & spec) );

    return 0;
}

在 Linux(Debian 9、x86_64)上:

[STEP 101] # uname -a
Linux debian9 4.9.0-6-amd64 #1 SMP Debian 4.9.88-1 (2018-04-29) x86_64 GNU/Linux
[STEP 102] # gcc foo.c && ./a.out
_POSIX_MONOTONIC_CLOCK         = 0
sysconf(_SC_MONOTONIC_CLOCK)   = 200809
clock_gettime(CLOCK_MONOTONIC) = 0
[STEP 103] #

在 macOS(10.13,High Sierra)上:

[STEP 201] $ uname -a
Darwin macbook.home 17.5.0 Darwin Kernel Version 17.5.0: Fri Apr 13 19:32:32 PDT 2018; root:xnu-4570.51.2~1/RELEASE_X86_64 x86_64
[STEP 202] $ cc foo.c && ./a.out
_POSIX_MONOTONIC_CLOCK         = -1
sysconf(_SC_MONOTONIC_CLOCK)   = -1
clock_gettime(CLOCK_MONOTONIC) = 0
[STEP 203] $

在 FreeBSD(11.1,x86_64)上:

[STEP 301] # uname -a
FreeBSD freebsd 11.1-RELEASE FreeBSD 11.1-RELEASE #0 r321309: Fri Jul 21 02:08:28 UTC 2017     root@releng2.nyi.freebsd.org:/usr/obj/usr/src/sys/GENERIC  amd64
[STEP 302] # cc foo.c && ./a.out 
_POSIX_MONOTONIC_CLOCK         = 200112
sysconf(_SC_MONOTONIC_CLOCK)   = 200112
clock_gettime(CLOCK_MONOTONIC) = 0
[STEP 303] #

macOS 上的结果让我很惊讶。 sysconf() 返回 -1clock_gettime(CLOCK_MONOTONIC) 成功!不确定这是否表明 macOS 不符合 POSIX。无论如何,它证明 使用 sysconf() 进行运行时检查是不可靠的!

最后我要这样做了:

int
Clock_gettime(struct timespec * spec)
{
    static bool firstime = true;
    static clockid_t clock = CLOCK_REALTIME;

    if (firstime) {
        firstime = false;
#ifdef CLOCK_MONOTONIC
        if (clock_gettime(CLOCK_MONOTONIC, spec) == 0) {
            clock = CLOCK_MONOTONIC;
            return 0;
        }
#endif
    }

    return clock_gettime(clock, spec);
}

【讨论】:

  • 得知 OSX 10.13 确实有 clock_gettime 和 CLOCK_MONOTONIC,我感到很惊喜。关联的 _POSIX__SC_ 测试表明它没有,这是一个普通的老错误。
猜你喜欢
  • 2023-03-16
  • 1970-01-01
  • 1970-01-01
  • 2011-10-17
  • 1970-01-01
  • 2021-09-06
  • 2021-11-05
  • 2010-12-17
  • 2011-08-12
相关资源
最近更新 更多