Unix/Linux编程:POSIX时钟

POSIX时钟所提供的时钟访问API可以支持纳秒级的时间精度,其中标识纳秒级时间值的timespec结构同样也用于nanosleep()调用

Linux 中,调用此 API 的程序必须以-lrt 选项进行编译,从而与 librt(realtime,实时)函数库相链接

获取以及设置时钟

POSIX 时钟 API 的主要系统调用包括获取时钟当前值的 clock_gettime()、返回时钟分辨率的 clock_getres(),以及更新时钟的 clock_settime()

NAME
       clock_getres, clock_gettime, clock_settime - clock and time functions

SYNOPSIS
       #include <time.h>

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

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

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

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

       clock_gettime()clock_settime() 检索并设置指定时钟clk_id的时间。

       res和tp参数是timespec结构:

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


系统调用 clock_gettime()针对参数 clockid 所指定的时钟返回时间。 返回的时间值置于tp指针所指向的timespec结构中。虽然 timespec 结构提供了纳秒级精度,但 clock_gettime()返回的时间值粒度可能还是要更大一点。系统调用 clock_getres()在参数 res
中返回指向 timespec 结构的指针,其中包含了由 clockid 所指定时钟的分辨率。

clockid_t 是一种由 SUSv3 定义的数据类型,用于表示时钟标识符,其值如下:

clockid_t描述
CLOCK_REALTIME可设定的系统级实时时钟
CLOCK_MONOTONIC不可设定的恒定态时钟
CLOCK_PROCESS_CPUTIME_ID每进程 CPU 时间的时钟(自 Linux 2.6.12)
CLOCK_THREAD_CPUTIME_ID每线程 CPU 时间的时钟(自 Linux 2.6.12)
  • CLOCK_REALTIME 时钟是一种系统级时钟,用于度量真实时间。与CLOCK_MONOTONIC 时钟不同,它的设置是可以变更的。
  • SUSv3 规定,CLOCK_MONOTONIC时钟对时间的度量始于"未予规范的过去某一时点",系统启动后就不会发生改变。该时钟适用于那些无法容忍系统时钟发生跳跃性变化(例如:手工改变了系统时间)的应用程序。Linux 上,这种时钟对时间的测量始于系统启动
  • CLOCK_PROCESS_CPUTIME_ID 时钟测量调用进程所消耗的用户和系统 CPU 时间。
  • CLOCK_THREAD_CPUTIME_ID 时钟的功用与之相类似,不过测量对象是进程中的单条线程
  • Linux 2.6.28 增加了一种新的时钟类型:CLOCK_MONOTONIC_RAW。类似于 CLOCK_ MONOTONIC,这也是一种无法设置的时钟,但是提供了对纯基于硬件时间的访问,且不受 NTP 时间调整的影响。这种非标准时钟适用于专业时钟同步应用程序。
  • Linux 2.6.35 又提供了两种新时钟:CLOCK_REALTIME_COARSE 和 CLOCK_MONTIC_ COARSE。这些时钟类似于 CLOCK_REALTIME 和 CLOCK_MONTONIC,适用于那些希望以最小代价获取较低分辨率时间戳的程序。这些非标准时钟不会引发对硬件时钟的任何访问(访问某些硬件时钟源的代价高昂),其返回值的分辨率为 jiffy(软件时钟周期)

系统调用 clock_settime()利用参数 tp 所指向缓冲区中的时间来设置由 clockid 指定的时钟。

  • 如果由tp指定的时间并非由clock_getres()所返回时钟分辨率的整数倍,时间会向下取整。
  • 特权(CAP_SYS_TIME)进程可以设置 CLOCK_REALTIME 时钟。该时钟的初始值通常是自 Epoch(1970 年 1 月 1 日 0 点 0 分 0 秒)以来的时间。

获取特定进程或线程的时钟ID

要测量特定进程或者线程所消耗的CPU时间,首先可以借助下面函数来获取其时钟ID,接着再以此返回 id 去调用 clock_gettime(),从而获得进程或线程耗费的 CPU 时间

函数 clock_getcpuclockid()会将隶属于 pid 进程的 CPU 时间时钟的标识符置于 clockid 指针所指向的缓冲区中。

NAME
       clock_getcpuclockid - 获取进程CPU时钟的ID

SYNOPSIS
       #include <time.h>

       int clock_getcpuclockid(pid_t pid, clockid_t *clock_id);

     

DESCRIPTION
       获取ID为pid的进程的CPU时钟的ID,并将其返回到clock_id所指向的位置
       如果pid为零,则返回调用进程的CPU时钟的时钟ID。

RETURN VALUE
       成功时,clock\u getcpuclockid()返回0;出错时,它返回错误中列出的一个正错误号。

函数 pthread_getcpuclockid()是 clock_getcpuclockid()的 POSIX 线程版,返回的标识符所标
识的时钟用于度量调用进程中指定线程消耗的 CPU 时间。

NAME
       pthread_getcpuclockid - 检索线程的CPU时钟ID
       
SYNOPSIS
       #include <pthread.h>
       #include <time.h>

       int pthread_getcpuclockid(pthread_t thread, clockid_t *clock_id);

      编译并使用-pthread链接。

DESCRIPTION
       pthread_getcpuclockid()  函数返回线程的CPU时钟的时钟ID。

RETURN VALUE
       成功时,此函数返回0;出错时,它返回一个非零的错误号。

高分辨率休眠的改进版:clock_nanosleep()

类似于nanosleep(),Linux特有的clock_nanosleep()系统调用也可以暂停调用进程,直到历经一段指定的时间间隔后,或者是收到信号才恢复运行。

NAME
       clock_nanosleep - high-resolution sleep with specifiable clock

SYNOPSIS
       #include <time.h>

       int clock_nanosleep(clockid_t clock_id, int flags,
                           const struct timespec *request,
                           struct timespec *remain);

       Link with -lrt (only for glibc versions before 2.17).

参数 request 及 remain 同 nanosleep()中的对应参数目的相似。

  • 默认情况下(即flags为0),由request指定的休眠间隔时间是相对时间
  • 如果在flags中设定了TIMER_ABSTIME,request则表示clockid时钟所测量的绝对时间。

这一特性对于那些需要精确休眠一段指定时间的应用程序序至关重要。如果只是先获取当前时间,计算与目标时间的差距,再以相对时间进行休眠,进程可能执行到一半就被占先了(优先级更高的任务抢占了),结果休眠时间比预期的要久

对于那些被信号处理函数中断并使用循环重启休眠的进程来说,“嗜睡(oversleeping)”问题尤其明显。如果以高频率接收信号,那么按相对时间休眠(nanosleep()所执行的类型)的进程在休眠时间上会有较大误差。但可以通过如下方式来避免嗜睡问题:先调用 clock_gettime()获取时间,加上期望休眠的时间量,再以 TIMER_ABSTIME 标志调用clock_nanosleep()函数(并且,如果被信号处理器中断,则会重启系统调用)。

指定TIMER_ABSTIME时,不再(也不需要)使用参数remain。如果信号处理器程序中断了clock_nanosleep()调用,再次调用该函数来重启休眠时,request参数不变。

clock_nanosleep()与 nanosleep()区分开来的另一特性在于,可以选择不同的时钟来测量休眠间隔时间。可在 clockid 中指定所期望的时钟 CLOCK_REALTIME、CLOCK_ MONOTONIC或 CLOCK_PROCESS_CPUTIME_ID。

下面使用了clock_nanosleep来避免过度睡眠

struct timespec request;
/* Retrieve current value of CLOCK_REALTIME clock */
if (clock_gettime(CLOCK_REALTIME, &request) == -1) 
    errExit("clock_gettime");
request.tv_sec += 20;               /* Sleep for 20 seconds from now */
s = clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &request, NULL); 
if (s != 0) {
    if (s == EINTR)
        printf("Interrupted by signal handler\n");
    else
        errExitEN(s, "clock_nanosleep");
}

例程

clock_gettime

#include <time.h>
#include <stdio.h>
#include <unistd.h>
 
int main(int argc, char **argv)
{
    struct timespec time1 = {0, 0}; 
    struct timespec time2 = {0, 0};
	
    float temp;
 
    clock_gettime(CLOCK_REALTIME, &time1);      
    usleep(1000);
    clock_gettime(CLOCK_REALTIME, &time2);   
    temp = (time2.tv_nsec - time1.tv_nsec) / 1000000;
    printf("time = %f ms\n", temp);
    return 0;
}

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值