clock_gettime()函数详解

内容分享2天前发布
0 0 0


clock_gettime()
是 POSIX 标准定义的一个高精度计时函数,用于获取指定时钟类型的当前时间。它的精度可以达到纳秒级,被广泛应用于需要精确时间测量或时间戳记录的场景(如性能分析、实时系统、音视频同步等)。


而同样是获取当前时间的clock()函数
的精度通常为毫秒级(具体取决于系统),对于短耗时操作,可能无法准确捕捉时间差异。所以
clock_gettime()是最优选择。

以下从函数原型、参数、时钟类型、返回值、使用场景及注意事项等方面详细说明:

1. 函数原型与头文件



#include <time.h>
 
int clock_gettime(clockid_t clk_id, struct timespec *tp);

头文件​:需包含
<time.h>
以使用函数声明和
struct timespec
结构体。

参数​:


clk_id
:指定要获取的时钟类型(预定义的时钟标识符)。


tp
:指向
struct timespec
结构体的指针,用于存储获取到的时间。

返回值​:

成功时返回
0

失败时返回
-1
,并设置
errno
指示错误原因(如
EINVAL
表示无效的
clk_id

EFAULT
表示
tp
指针无效)。

2.
struct timespec
结构体

该结构体用于存储时间,包含两个成员:



struct timespec {
    time_t tv_sec;   // 秒数(自某个纪元以来的整数秒)
    long   tv_nsec;  // 纳秒数(0 ≤ tv_nsec < 1,000,000,000)
};

通过
tv_sec

tv_nsec
的组合,
clock_gettime()
可以提供 ​纳秒级精度​(实际精度取决于硬件和系统实现),远高于传统
time()
函数(秒级)或
gettimeofday()
函数(微秒级)。

3. 时钟类型(
clk_id
常见取值)​


clk_id
决定了函数获取的是哪种时钟的时间,常见类型如下:

​(1) CLOCK_REALTIME(实时时钟)​

定义​:系统范围的“真实世界”时间,通常对应日历时间(年/月/日 时:分:秒)。

特点​:

受系统时间手动调整(如
date
命令)或 NTP(网络时间协议)同步的影响,可能向前或向后跳跃。

系统重启后重置(基于 RTC 或网络时间同步)。

典型用途​:需要与实际日期时间关联的场景(如日志记录、文件时间戳)。

​(2) CLOCK_MONOTONIC(单调时钟)​

定义​:从某个固定起点(如系统启动或首次调用)开始的单调递增时间,​不受系统时间调整影响

特点​:

时间值只会递增(即使系统时间被手动调前),适合测量时间间隔(如程序执行耗时)。

起点由系统决定(通常为系统启动时间),但不同系统的起点可能不同。

Linux 中,该时钟的起点通常是系统启动时刻(可通过
/proc/uptime
验证)。

典型用途​:性能测试、计时器、需要稳定时间间隔的场景。

​(3) CLOCK_PROCESS_CPUTIME_ID(进程 CPU 时间)​

定义​:当前进程自启动以来实际占用的 CPU 时间(仅统计进程在 CPU 上运行的时间,不包括等待 I/O 或睡眠的时间)。

特点​:

每个进程独立,不同进程的该时钟值无关。

进程终止后重置。

典型用途​:统计进程自身的 CPU 资源消耗(如性能分析)。

​(4) CLOCK_THREAD_CPUTIME_ID(线程 CPU 时间)​

定义​:当前线程自启动以来实际占用的 CPU 时间(仅统计该线程在 CPU 上运行的时间)。

特点​:

每个线程独立,不同线程的该时钟值无关。

线程终止后重置。

典型用途​:多线程程序中统计单个线程的 CPU 消耗。

​(5) CLOCK_BOOTTIME(Linux 特有,包含挂起时间的实时时钟)​

定义​:类似
CLOCK_REALTIME
,但包含系统挂起(睡眠)的时间,即从系统启动到当前的总时间(无论是否处于运行状态)。

特点​:

系统挂起时仍会递增(因为挂起期间系统时间可能停止,但
CLOCK_BOOTTIME
记录的是“真实经过的时间”)。

适用于需要统计系统总运行时间(包括睡眠)的场景(如电池续航计算)。

注意​:该时钟是 Linux 扩展,非 POSIX 标准,跨平台时需谨慎。

4. 使用示例

以下是几个典型场景的使用示例:

示例 1:获取当前实时时钟时间(日历时间)​


#include <stdio.h>
#include <time.h>
 
int main() {
    struct timespec ts;
    if (clock_gettime(CLOCK_REALTIME, &ts) == -1) {
        perror("clock_gettime");
        return 1;
    }
 
    // 转换为可读的时间格式(需结合 localtime 或 gmtime)
    struct tm *local_time = localtime(&ts.tv_sec);
    printf("Current time: %04d-%02d-%02d %02d:%02d:%02d.%09ld
",
           local_time->tm_year + 1900, local_time->tm_mon + 1, local_time->tm_mday,
           local_time->tm_hour, local_time->tm_min, local_time->tm_sec, ts.tv_nsec);
    return 0;
}
示例 2:测量代码执行时间(使用 CLOCK_MONOTONIC)​


#include <stdio.h>
#include <time.h>
#include <unistd.h>  // for usleep()
 
int main() {
    struct timespec start, end;
    if (clock_gettime(CLOCK_MONOTONIC, &start) == -1) {
        perror("clock_gettime start");
        return 1;
    }
 
    // 模拟耗时操作(睡眠 100ms)
    usleep(100000);
 
    if (clock_gettime(CLOCK_MONOTONIC, &end) == -1) {
        perror("clock_gettime end");
        return 1;
    }
 
    // 计算时间差(纳秒转毫秒)
    long diff_ns = (end.tv_sec - start.tv_sec) * 1000000000L + (end.tv_nsec - start.tv_nsec);
    double diff_ms = diff_ns / 1000000.0;
    printf("Elapsed time: %.3f ms
", diff_ms);  // 输出约 100.0ms
    return 0;
}
示例 3:获取进程 CPU 时间


#include <stdio.h>
#include <time.h>
#include <unistd.h>  // for sleep()
 
int main() {
    struct timespec cpu_start, cpu_end;
 
    if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &cpu_start) == -1) {
        perror("clock_gettime");
        return 1;
    }
 
    // 模拟进程运行(包含睡眠,睡眠时间不计入 CPU 时间)
    sleep(1);  // 睡眠 1 秒(不占用 CPU)
 
    if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &cpu_end) == -1) {
        perror("clock_gettime");
        return 1;
    }
 
    long diff_ns = (cpu_end.tv_sec - cpu_start.tv_sec) * 1000000000L + (cpu_end.tv_nsec - cpu_start.tv_nsec);
    double diff_ms = diff_ns / 1000000.0;
    printf("Process CPU time: %.3f ms
", diff_ms);  // 输出接近 0ms(因 sleep 不占 CPU)
    return 0;
}

5. 注意事项

编译链接​:部分旧系统(如早期的 Linux)可能需要链接实时库
-lrt
(通过
-lrt
编译选项),但现代 Linux 内核(如 2.6.28 之后)已内置支持,无需额外链接。

精度限制​:虽然
timespec
支持纳秒,但实际精度受硬件(如定时器分辨率)和系统调度影响。例如,x86 系统的定时器通常为 100Hz(10ms)或 1MHz(1μs),ARM 系统可能更高,但难以达到纳秒级。

可移植性​:
clk_id
的具体取值可能因系统而异(如
CLOCK_BOOTTIME
是 Linux 特有),跨平台开发时需检查目标系统的支持情况(可通过
sysconf(_SC_CLOCK_GETTIME_RES)
查询最小时间间隔)。

线程安全​:
clock_gettime()
本身是线程安全的,但
struct timespec
存储的内存需由调用者保证不被并发修改。

替代函数​:在 Linux 中,
gettimeofday()
已被标记为过时(POSIX.1-2008 弃用),推荐使用
clock_gettime()
替代。

总结


clock_gettime()
是 POSIX 标准中高精度计时的核心函数,通过支持多种时钟类型(如实时时钟、单调时钟、CPU 时间等),能够满足不同场景的时间测量需求。在嵌入式 Linux 开发中,合理选择时钟类型(例如用
CLOCK_MONOTONIC
测量帧处理延迟,用
CLOCK_REALTIME
同步音视频时间戳)是关键。

clock_gettime()函数详解

秋深的银杏叶,像是人们未寄完的信笺

© 版权声明

相关文章

暂无评论

none
暂无评论...