一文搞懂应用开发所需 Linux 系统时间的相关知识点
秒级精度
time_t
保存自 UTC 1970 年 1 月 1 日 00:00 以来的秒数(不包括闰秒),对应于
POSIX time
Unix 和 POSIX 兼容系统将 time_t
类型作为带符号整数(通常为 32 或 64 位宽)来实现,它表示自 Unix 时间开始以来的秒数
time_t 和 struct tm 之间的转换接口
struct tm *localtime(const time_t *timep);
struct tm *localtime_r(const time_t *timep, struct tm *result);
time_t mktime(struct tm *tm);
struct tm {
int tm_sec; /* Seconds (0-60) */
int tm_min; /* Minutes (0-59) */
int tm_hour; /* Hours (0-23) */
int tm_mday; /* Day of the month (1-31) */
int tm_mon; /* Month (0-11) */
int tm_year; /* Year - 1900 */
int tm_wday; /* Day of the week (0-6, Sunday = 0) */
int tm_yday; /* Day in the year (0-365, 1 Jan = 0) */
int tm_isdst; /* Daylight saving time */
};
获取当前时间的 time_t
#include <time.h>
NAME
time - get time in seconds
SYNOPSIS
#include <time.h>
time_t time(time_t *tloc);
DESCRIPTION
time() returns the time as the number of seconds since the Epoch, 1970-01-01 00:00:00 +0000 (UTC).
If tloc is non-NULL, the return value is also stored in the memory pointed to by tloc.
当前时间转 struct tm
打印
std::tm* local_time = localtime(¤t_time);
// Print the local time
std::cout << "Current local time is: "
<< (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 << std::endl;
纳秒精度 struct timespec
struct timespec {
time_t tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
int clock_gettime(clockid_t clk_id, struct timespec *tp);
int clock_settime(clockid_t clk_id, const struct timespec *tp);
关于 clk_id
clk_id 参数是要对其采取行动的特定时钟的标识符。时钟可以是系统范围的,因此对所有进程都可见,或者如果它仅在单个进程内测量时间,则可以对每个进程都可见。
所有实现都支持系统范围的实时时钟,该时钟由 CLOCK_REALTIME 标识。其时间表示自纪元以来的秒数和纳秒数。当其时间改变时,相对间隔的计时器不受影响,但绝对时间点的计时器会受到影响。
可以实现更多时钟。相应时间值的解释和对计时器的影响尚未指定。
足够新的 glibc 版本和 Linux 内核支持以下时钟:
CLOCK_REALTIME
测量实际(即挂钟)时间的系统范围时钟。设置此时钟需要适当的权限。此时钟受系统时间不连续跳跃的影响(例如,如果系统管理员手动更改时钟),以及 adjtime(3) 和 NTP 执行的增量调整。
CLOCK_REALTIME_COARSE(自 Linux 2.6.32 起;特定于 Linux)
CLOCK_REALTIME 的更快但精度较低的版本。当您需要非常快但不是细粒度的时间戳时使用。需要每个架构支持,并且可能还需要 vdso(7) 中对此标志的架构支持。
CLOCK_MONOTONIC
无法设置的时钟,表示自 POSIX 描述的“过去某个未指定的点”以来的单调时间。在 Linux 上,该点对应于系统自启动以来运行的秒数。
CLOCK_MONOTONIC 时钟不受系统时间不连续跳跃的影响(例如,如果系统管理员手动更改时钟),但会受到 adjtime(3) 和 NTP 执行的增量调整的影响。此时钟不计算系统暂停的时间。
CLOCK_MONOTONIC_COARSE(自 Linux 2.6.32 起;Linux 专用)
CLOCK_MONOTONIC 的更快但精度更低的版本。当您需要非常快速但不是细粒度的时间戳时使用。需要每个架构支持,并且可能还需要 vdso(7) 中对此标志的架构支持。
CLOCK_MONOTONIC_RAW(自 Linux 2.6.28 起;Linux 专用)
与 CLOCK_MONOTONIC 类似,但提供对不受 NTP 调整或 adjtime(3) 执行的增量调整影响的原始硬件时间的访问。此时钟不计算系统暂停的时间。
CLOCK_BOOTTIME(自 Linux 2.6.39 起;Linux 专用)
与 CLOCK_MONOTONIC 相同,但它还包括系统暂停的任何时间。这允许应用程序获得可感知暂停的单调时钟,而无需处理 CLOCK_REALTIME 的复杂性,如果使用 settimeofday(2) 或类似方法更改时间,则可能会出现不连续性。
CLOCK_PROCESS_CPUTIME_ID(自 Linux 2.6.12 起)
每个进程的 CPU 时间时钟(测量进程中所有线程所消耗的 CPU 时间)。
CLOCK_THREAD_CPUTIME_ID(自 Linux 2.6.12 起)
线程专用的 CPU 时间时钟。
根据时间戳设置系统时间
// Your existing code to get the timestamp_us
uint64_t timestamp_us = 1629374305000000; // Example timestamp in microseconds
// Convert the timestamp to seconds and microseconds
struct timespec new_time;
new_time.tv_sec = static_cast<time_t>(timestamp_us / 1000000);
new_time.tv_nsec = (timestamp_us % 1000000) * 1000;
// CLOCK_REALTIME is the identifier for the system-wide realtime clock
int result = clock_settime(CLOCK_REALTIME, &new_time);
if (result == -1) {
std::cerr << "Failed to set system time: " << strerror(errno) << std::endl;
return 1;
}
std::cout << "System time updated to: "
<< new_time.tv_sec << "s and " << new_time.tv_nsec << "ns" << std::endl;
微秒精度 struct timeval
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
};
struct timezone {
int tz_minuteswest; /* minutes west of Greenwich */
int tz_dsttime; /* type of DST correction */
};
#include <sys/time.h>
int gettimeofday(struct timeval *tv, struct timezone *tz);
int settimeofday(const struct timeval *tv, const struct timezone *tz);
struct timeval
和 struct timespec
都是POSIX标准中定义的时间结构体,用于表示时间点或时间间隔。它们在很多系统调用中用于获取或设置时间。以下是这两个结构体的详细说明:
struct timeval
struct timeval
用于表示一个时间间隔,它包含两个字段:
tv_sec
: 表示秒(time_t
类型),time_t
通常是一个长整型(long
),表示自Unix纪元(1970年1月1日 00:00:00 UTC)以来的秒数。tv_usec
: 表示微秒(suseconds_t
类型,通常是一个长整型),表示tv_sec
后的微秒数。
这个结构体通常用于需要时间间隔的场景,比如在某些系统调用中指定超时时间。
struct timespec
struct timespec
用于表示一个具体的时间点,它同样包含两个字段:
tv_sec
: 表示秒(time_t
类型),同struct timeval
中的tv_sec
。tv_nsec
: 表示纳秒(long
类型),表示tv_sec
后的纳秒数。
struct timespec
提供了比 struct timeval
更高的时间精度(纳秒级对比微秒级),因此它更适合用于需要高分辨率时间点的场景。
使用场景
struct timeval
常用于设置或获取某个操作的超时时间,例如在使用select()
,poll()
或者gettimeofday()
函数时。struct timespec
常用于需要设定或查询具体时间点的场合,例如使用clock_gettime()
或clock_settime()
函数。
#include <iostream>
#include <ctime>
#include <sys/time.h>
int main() {
// 使用struct timeval获取当前时间
struct timeval now_val;
if (gettimeofday(&now_val, NULL) == -1) {
perror("gettimeofday");
return 1;
}
std::cout << "Current time (usec): "
<< now_val.tv_sec << "s and " << now_val.tv_usec << "us since the epoch" << std::endl;
// 使用struct timespec获取当前时间
struct timespec now_spec;
if (clock_gettime(CLOCK_REALTIME, &now_spec) == -1) {
perror("clock_gettime");
return 1;
}
std::cout << "Current time (nsec): "
<< now_spec.tv_sec << "s and " << now_spec.tv_nsec << "ns since the epoch" << std::endl;
return 0;
}