linux 如何检查系统是否支持“单调时钟”?

a1o7rhls  于 2022-11-22  发布在  Linux
关注(0)|答案(3)|浏览(179)

我需要在代码中处理超时情况,如果系统支持Monotonic Clock,我希望使用clock_gettime(CLOCK_MONOTONIC)

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

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

vngu2lb8

vngu2lb81#

根据POSIX的规定,即使定义了常量CLOCK_MONOTONIC,您实际上也可能需要运行时测试。处理此问题的官方方法是使用_POSIX_MONOTONIC_CLOCK“feature-test宏”,但这些宏具有非常复杂的语义:引用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的操作系统(例如,据我所知,MacOSX仍然没有它),而不是一个有clock_gettime但没有CLOCK_MONOTONIC的操作系统。

yks3o0rb

yks3o0rb2#

POSIX只要求存在CLOCK_REALTIME,其他时钟是可选的。
如果单调时钟可用,宏_POSIX_MONOTONIC_CLOCK将在unistd.h中定义(根据man pageAvailability部分)

ctehm74n

ctehm74n3#

正如@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,高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()返回-1,但是clock_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);
}

相关问题