CLOCK_GETRES - Linux手册页

时间:2019-08-20 17:58:36  来源:igfitidea点击:

Linux程序员手册 第2部分
更新日期: 2020-08-13

名称

clock_getres,clock_gettime,clock_settime-时钟和时间函数

语法

#包括

int clock_getres(clockid_t clockid,struct timespec * res);

int clock_gettime(clockid_t clockid,struct timespec * tp);

int clock_settime(clockid_t clockid,const struct timespec * tp);

与-lrt链接(仅适用于2.17之前的glibc版本)。

glibc的功能测试宏要求(请参阅feature_test_macros(7)):

clock_getres(),clock_gettime(),clock_settime():

_POSIX_C_SOURCE>= 199309L

说明

函数clock_getres()查找指定时钟Clockid的分辨率(精度),如果res为非NULL,则将其存储在res指向的struct timespec中。时钟的分辨率取决于实现方式,无法由特定进程配置。如果clock_settime()的参数tp指向的时间值不是res的倍数,则将其截断为res的倍数。

函数clock_gettime()和clock_settime()检索并设置指定时钟clockid的时间。

res和tp参数是timespec结构,如中所指定:

struct timespec {
    time_t   tv_sec;        /* seconds */
    long     tv_nsec;       /* nanoseconds */
};

clockid参数是要对其执行操作的特定时钟的标识符。时钟可能是系统范围的,因此对于所有进程都是可见的,如果仅在单个进程中测量时间,则每个进程都可以看到。

所有实现都支持系统范围的实时时钟,该时钟由CLOCK_REALTIME标识。它的时间代表自纪元以来的秒和纳秒。更改时间后,相对间隔的计时器不会受到影响,但是绝对时间点的计时器会受到影响。

可以实现更多时钟。未说明相应时间值的解释以及对计时器的影响。

最新版本的glibc和Linux内核支持以下时钟:

CLOCK_REALTIME
可设置的系统范围时钟,用于测量实际(即挂钟)时间。设置此时钟需要适当的特权。此时钟受系统时间的不连续跳跃(例如,如果系统管理员手动更改时钟)以及adjtime(3)和NTP执行的增量调整的影响。
CLOCK_REALTIME_ALARM(since Linux 3.0; Linux-specific)
类似于CLOCK_REALTIME,但不可设置。有关更多详细信息,请参见timer_create(2)。
CLOCK_REALTIME_COARSE(since Linux 2.6.32; Linux-specific)
CLOCK_REALTIME的更快但不太精确的版本。该时钟不可设置。当您需要非常快速但不需要细粒度的时间戳时使用。需要每个体系结构支持,并且可能还需要vdso(7)中的此标志的体系结构支持。
CLOCK_TAI(since Linux 3.10; Linux-specific)
从壁钟时间派生而来的不可设置的系统范围时钟,但忽略leap秒。此时钟不会像LOCK_REALTIME一样经历NTP插入leap秒引起的不连续和向后跳动。
TAI的首字母缩写是指国际原子时间。
CLOCK_MONOTONIC
一个不可设置的系统范围的时钟,它表示自POSIX以来的单调时间,如POSIX所述。在Linux上,该点对应于系统自启动以来已运行的秒数。
CLOCK_MONOTONIC时钟不受系统时间的不连续跳跃影响(例如,如果系统管理员手动更改时钟),但会受到adjtime(3)和NTP执行的增量调整的影响。该时钟不计算系统挂起的时间。所有CLOCK_MONOTONIC变体都保证连续调用返回的时间不会倒退,但是连续调用(取决于体系结构)可能会返回相同(而不增加)的时间值。
CLOCK_MONOTONIC_COARSE(since Linux 2.6.32; Linux-specific)
CLOCK_MONOTONIC的更快但不太精确的版本。当您需要非常快速但不需要细粒度的时间戳时使用。需要每个体系结构支持,并且可能还需要vdso(7)中的此标志的体系结构支持。
CLOCK_MONOTONIC_RAW(since Linux 2.6.28; Linux-specific)
与CLOCK_MONOTONIC相似,但是提供对基于硬件的原始时间的访问,该时间不受NTP调整或adjtime(3)执行的增量调整的影响。该时钟不计算系统挂起的时间。
CLOCK_BOOTTIME(since Linux 2.6.39; Linux-specific)
与CLOCK_MONOTONIC相同的不可设置的系统范围时钟,除了它还包括系统挂起的任何时间。这允许应用程序获得可感知暂停的单调时钟,而不必处理CLOCK_REALTIME的复杂性,如果使用settimeofday(2)或类似的时间更改了时间,则可能会出现中断。
CLOCK_BOOTTIME_ALARM(since Linux 3.0; Linux-specific)
就像CLOCK_BOOTTIME。有关更多详细信息,请参见timer_create(2)。
CLOCK_PROCESS_CPUTIME_ID(since Linux 2.6.12)
这是一个时钟,用于测量此进程消耗的CPU时间(即,该进程中所有线程消耗的CPU时间)。在Linux上,此时钟不可设置。
CLOCK_THREAD_CPUTIME_ID(since Linux 2.6.12)
这是一个时钟,用于测量此线程消耗的CPU时间。在Linux上,此时钟不可设置。

Linux还实现了动态时钟实例,如下所述。

Dynamic clocks

除了上述硬编码的System-V风格的时钟ID外,Linux还支持某些字符设备上的POSIX时钟操作。此类设备称为"动态"时钟,自Linux 2.6.39开始受支持。

使用适当的宏,打开的文件描述符可以转换为时钟ID,并传递给clock_gettime(),clock_settime()和clock_adjtime(2)。下面的示例显示如何将文件描述符转换为动态时钟ID。

#define CLOCKFD 3
#define FD_TO_CLOCKID(fd)   ((ti(clockid_t) (fd) << 3) | CLOCKFD)
#define CLOCKID_TO_FD(clk)  ((unsigned int) ti((clk) >> 3))

struct timeval tv;
clockid_t clkid;
int fd;

fd = open("/dev/ptp0", O_RDWR);
clkid = FD_TO_CLOCKID(fd);
clock_gettime(clkid, &tv);

返回值

clock_gettime(),clock_settime()和clock_getres()对于成功返回0,对于失败返回-1(在这种情况下,正确设置了errno)。

错误说明

EFAULT
tp指向可访问地址空间之外。
EINVAL
由于两个原因之一,指定的时钟标识无效。 System-V样式的硬编码正值超出范围,或者动态时钟ID未引用时钟对象的有效实例。
EINVAL
(clock_settime()):tp.tv_sec为负或tp.tv_nsec不在[0..999,999,999]范围内。
EINVAL
调用clock_settime()时指定的Clockid不是可设置的时钟。
ENOTSUP
指定的动态POSIX时钟设备不支持该操作。
EINVAL(since Linux 4.3)
调用时标为CLOCK_REALTIME的clock_settime()尝试将时间设置为小于CLOCK_MONOTONIC时钟的当前值的值。
ENODEV
由动态clk_id表示的可热插拔设备(例如USB)在其字符设备打开后已消失。
EPERM
clock_settime()没有设置指示时钟的权限。
EACCES
clock_settime()对指示的动态POSIX时钟设备没有写权限。

版本

这些系统调用首先出现在Linux 2.6中。

属性

有关本节中使用的术语的说明,请参见attribute(7)。

InterfaceAttributeValue
clock_getres(),clock_gettime(),clock_settime()Thread safetyMT-Safe

遵循规范

POSIX.1-2001,POSIX.1-2008,SUSv2。

在具有这些功能的POSIX系统上,符号_POSIX_TIMERS定义为大于0的值。符号_POSIX_MONOTONIC_CLOCK_POSIX_CPUTIME,_POSIX_THREAD_CPUTIME表示CLOCK_MONOTONIC,CLOCK_PROCESS_CPUTIME_ID和CLOCK_THREAD_CPUTIME_ID可用。 (另请参见sysconf(3)。)

备注

POSIX.1指定以下内容:

通过clock_settime()设置CLOCK_REALTIME时钟的值对基于该时钟被阻止等待相关时间服务的线程无效,包括nanosleep()函数;也不会基于此时钟的相对计时器到期。因此,这些时间服务将在请求的相对间隔过去时终止,而与时钟的新值或旧值无关。

根据POSIX.1-2001,具有"适当特权"的进程可以使用clock_settime()设置CLOCK_PROCESS_CPUTIME_ID和CLOCK_THREAD_CPUTIME_ID时钟。在Linux上,这些时钟不可设置(即,没有进程具有"适当的特权")。

C library/kernel differences

在某些体系结构上,vdso(7)中提供了clock_gettime()的实现。

Historical note for SMP systems

在Linux添加内核对CLOCK_PROCESS_CPUTIME_ID和CLOCK_THREAD_CPUTIME_ID的支持之前,glibc在许多平台上使用来自CPU的计时器寄存器(i386上的TSC,Itanium上的AR.ITC)实现了这些时钟。这些寄存器在CPU之间可能不同,因此,如果将进程迁移到另一个CPU,则这些时钟可能会返回假结果。

如果SMP系统中的CPU具有不同的时钟源,则由于每个CPU将以略有不同的频率运行,因此无法维持定时器寄存器之间的相关性。如果真是这样,那么clock_getcpuclockid(0)将返回ENOENT表示这种情况。只有确保可以确保进程停留在某个CPU上,这两个时钟才有用。

SMP系统中的处理器不会完全在同一时间启动,因此计时器寄存器通常以偏移量运行。一些体系结构包含试图在启动时限制这些偏移量的代码。但是,该代码不能保证精确调整偏移量。 Glibc不包含任何处理这些偏移量的规定(与Linux Kernel不同)。通常,这些偏移很小,因此在大多数情况下影响可以忽略不计。

从glibc 2.4开始,此页面中描述的系统调用的包装函数通过在提供了这种实现的系统(即Linux 2.6.12及更高版本)上采用CLOCK_PROCESS_CPUTIME_ID和CLOCK_THREAD_CPUTIME_ID的内核实现来避免上述问题。

示例

下面的程序演示了如何将clock_gettime()和clock_getres()与各种时钟一起使用。这是我们在运行程序时可能看到的示例:

$ ./clock_times x
CLOCK_REALTIME : 1585985459.446 (18356 days +  7h 30m 59s)
     resolution:          0.000000001
CLOCK_TAI      : 1585985496.447 (18356 days +  7h 31m 36s)
     resolution:          0.000000001
CLOCK_MONOTONIC:      52395.722 (14h 33m 15s)
     resolution:          0.000000001
CLOCK_BOOTTIME :      72691.019 (20h 11m 31s)
     resolution:          0.000000001

Program source

/* clock_times.c

   Licensed under GNU General Public License v2 or later.
*/
#define _XOPEN_SOURCE 600
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>

#define SECS_IN_DAY (24 * 60 * 60)

static void
displayClock(clockid_t clock, char *name, bool showRes)
{
    struct timespec ts;

    if (clock_gettime(clock, &ts) == -1) {
        perror("clock_gettime");
        exit(EXIT_FAILURE);
    }

    printf("%-15s: %10ld.%03ld (", name,
            (long) ts.tv_sec, ts.tv_nsec / 1000000);

    long days = ts.tv_sec / SECS_IN_DAY;
    if (days > 0)
        printf("%ld days + ", days);

    printf("%2ldh %2ldm %2lds", (ts.tv_sec % SECS_IN_DAY) / 3600,
            (ts.tv_sec % 3600) / 60, ts.tv_sec % 60);
    printf(")\n");

    if (clock_getres(clock, &ts) == -1) {
        perror("clock_getres");
        exit(EXIT_FAILURE);
    }

    if (showRes)
        printf("     resolution: %10ld.%09ld\n",
                (long) ts.tv_sec, ts.tv_nsec);
}

int
main(int argc, char *argv[])
{
    bool showRes = argc > 1;

    displayClock(CLOCK_REALTIME, "CLOCK_REALTIME", showRes);
#ifdef CLOCK_TAI
    displayClock(CLOCK_TAI, "CLOCK_TAI", showRes);
#endif
    displayClock(CLOCK_MONOTONIC, "CLOCK_MONOTONIC", showRes);
#ifdef CLOCK_BOOTTIME
    displayClock(CLOCK_BOOTTIME, "CLOCK_BOOTTIME", showRes);
#endif
    exit(EXIT_SUCCESS);
}

另外参见

date(1),gettimeofday(2),settimeofday(2),time(2),adjtime(3),clock_getcpuclockid(3),ctime(3),ftime(3),pthread_getcpuclockid(3),sysconf(3), time(7),time_namespaces(7),vdso(7),hwclock(8)

出版信息

这个页面是Linux手册页项目5.08版的一部分。有关项目的说明、有关报告错误的信息以及此页面的最新版本,请访问https://www.kernel.org/doc/man-pages/