是 POSIX 标准定义的一个高精度计时函数,用于获取指定时钟类型的当前时间。它的精度可以达到纳秒级,被广泛应用于需要精确时间测量或时间戳记录的场景(如性能分析、实时系统、音视频同步等)。
clock_gettime()
的精度通常为毫秒级(具体取决于系统),对于短耗时操作,可能无法准确捕捉时间差异。所以
而同样是获取当前时间的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
该结构体用于存储时间,包含两个成员:
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
决定了函数获取的是哪种时钟的时间,常见类型如下:
clk_id
(1) CLOCK_REALTIME(实时时钟)
定义:系统范围的“真实世界”时间,通常对应日历时间(年/月/日 时:分:秒)。
特点:
受系统时间手动调整(如
命令)或 NTP(网络时间协议)同步的影响,可能向前或向后跳跃。
date
系统重启后重置(基于 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
编译选项),但现代 Linux 内核(如 2.6.28 之后)已内置支持,无需额外链接。
-lrt
精度限制:虽然
支持纳秒,但实际精度受硬件(如定时器分辨率)和系统调度影响。例如,x86 系统的定时器通常为 100Hz(10ms)或 1MHz(1μs),ARM 系统可能更高,但难以达到纳秒级。
timespec
可移植性:
的具体取值可能因系统而异(如
clk_id
是 Linux 特有),跨平台开发时需检查目标系统的支持情况(可通过
CLOCK_BOOTTIME
查询最小时间间隔)。
sysconf(_SC_CLOCK_GETTIME_RES)
线程安全:
本身是线程安全的,但
clock_gettime()
存储的内存需由调用者保证不被并发修改。
struct timespec
替代函数:在 Linux 中,
已被标记为过时(POSIX.1-2008 弃用),推荐使用
gettimeofday()
替代。
clock_gettime()
总结
是 POSIX 标准中高精度计时的核心函数,通过支持多种时钟类型(如实时时钟、单调时钟、CPU 时间等),能够满足不同场景的时间测量需求。在嵌入式 Linux 开发中,合理选择时钟类型(例如用
clock_gettime()
测量帧处理延迟,用
CLOCK_MONOTONIC
同步音视频时间戳)是关键。
CLOCK_REALTIME
秋深的银杏叶,像是人们未寄完的信笺