85. gettimeofday - 获取系统时间和时区信息 链接到标题
1. 函数介绍 链接到标题
gettimeofday
是一个 Linux 系统调用,用于获取当前系统的时间和时区信息。它提供了微秒级精度的时间获取能力,是传统的时间获取函数之一。虽然在新代码中推荐使用 clock_gettime()
,但 gettimeofday
仍然广泛使用。
2. 函数原型 链接到标题
#include <sys/time.h>
int gettimeofday(struct timeval *tv, struct timezone *tz);
注意:在现代 Linux 系统中,
tz
参数已被废弃,应该传入NULL
。
3. 功能 链接到标题
获取当前系统的时间信息,包括秒数和微秒数。时间从 Unix 纪元(1970年1月1日 00:00:00 UTC)开始计算。
4. 参数 链接到标题
struct timeval *tv
: 指向timeval
结构体的指针,用于存储时间信息struct timezone *tz
: 指向timezone
结构体的指针,用于存储时区信息(已废弃,应传入 NULL)
5. timeval 和 timezone 结构体定义 链接到标题
struct timeval {
time_t tv_sec; /* 秒数 */
suseconds_t tv_usec; /* 微秒数(0-999999)*/
};
struct timezone {
int tz_minuteswest; /* 格林威治以西的分钟数(已废弃)*/
int tz_dsttime; /* 夏令时类型(已废弃)*/
};
6. 返回值 链接到标题
- 成功时:返回 0
- 失败时:返回 -1,并设置
errno
- 注意:在现代 Linux 系统中,这个调用几乎不会失败
7. 常见 errno 错误码 链接到标题
EFAULT
:tv
或tz
指针指向无效内存地址EINVAL
:tz
参数不为NULL
(在某些系统上)
8. 相似函数,或关联函数 链接到标题
clock_gettime()
: 更现代的时间获取函数,支持纳秒精度time()
: 获取秒级时间戳ftime()
: 获取毫秒级时间(已废弃)clock()
: 获取处理器时间clock_getres()
: 获取时钟精度settimeofday()
: 设置系统时间(需要特权权限)strftime()
: 格式化时间字符串localtime()
,gmtime()
: 时间转换函数
9. 示例代码 链接到标题
示例1:基本使用 - 获取和显示时间 链接到标题
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <time.h>
#include <errno.h>
#include <string.h>
void print_timeval(const char *label, const struct timeval *tv) {
printf("%-20s %ld.%06ld 秒\n", label, tv->tv_sec, tv->tv_usec);
// 转换为可读格式
struct tm *tm_info = localtime(&tv->tv_sec);
if (tm_info) {
char time_str[64];
strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", tm_info);
printf("%-20s %s.%06ld\n", "可读格式:", time_str, tv->tv_usec);
}
}
int main() {
struct timeval tv;
int ret;
printf("=== gettimeofday 基本使用 ===\n");
// 获取当前时间
ret = gettimeofday(&tv, NULL);
if (ret == -1) {
perror("gettimeofday 失败");
exit(EXIT_FAILURE);
}
print_timeval("当前时间:", &tv);
// 与 time() 函数对比
time_t time_sec = time(NULL);
printf("%-20s %ld 秒\n", "time() 结果:", time_sec);
printf("%-20s %ld 秒\n", "gettimeofday 秒:", tv.tv_sec);
printf("%-20s %ld 微秒\n", "gettimeofday 微秒:", tv.tv_usec);
// 显示时区信息(虽然 tz 参数已废弃)
struct timezone tz;
ret = gettimeofday(&tv, &tz);
if (ret == 0) {
printf("\n时区信息(注意:已废弃):\n");
printf(" 西偏分钟数: %d\n", tz.tz_minuteswest);
printf(" 夏令时类型: %d\n", tz.tz_dsttime);
}
return 0;
}
示例2:时间精度和性能测试 链接到标题
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <unistd.h>
#include <math.h>
double timeval_diff(const struct timeval *end, const struct timeval *start) {
return (end->tv_sec - start->tv_sec) +
(end->tv_usec - start->tv_usec) / 1000000.0;
}
void precision_test() {
struct timeval tv1, tv2, tv3;
int i;
printf("=== 时间精度测试 ===\n");
// 连续调用测试精度
gettimeofday(&tv1, NULL);
gettimeofday(&tv2, NULL);
gettimeofday(&tv3, NULL);
printf("连续调用时间差:\n");
printf(" 调用1-调用2: %.9f 秒\n", timeval_diff(&tv2, &tv1));
printf(" 调用2-调用3: %.9f 秒\n", timeval_diff(&tv3, &tv2));
printf(" 调用1-调用3: %.9f 秒\n", timeval_diff(&tv3, &tv1));
// 微秒精度测试
printf("\n微秒精度测试:\n");
for (i = 0; i < 5; i++) {
gettimeofday(&tv1, NULL);
usleep(1000); // 1毫秒
gettimeofday(&tv2, NULL);
printf(" 实际延迟 %d: %.6f 秒\n", i + 1, timeval_diff(&tv2, &tv1));
}
}
void performance_benchmark() {
struct timeval start, end;
const int iterations = 1000000;
int i;
printf("\n=== 性能基准测试 ===\n");
printf("测试 %d 次 gettimeofday 调用...\n", iterations);
gettimeofday(&start, NULL);
for (i = 0; i < iterations; i++) {
struct timeval tv;
gettimeofday(&tv, NULL);
}
gettimeofday(&end, NULL);
double total_time = timeval_diff(&end, &start);
double avg_time = total_time / iterations * 1000000; // 转换为微秒
printf("结果:\n");
printf(" 总时间: %.6f 秒\n", total_time);
printf(" 平均每次调用: %.3f 微秒\n", avg_time);
printf(" 每秒调用次数: %.0f 次\n", iterations / total_time);
}
void comparison_with_other_functions() {
struct timeval tv_gettimeofday;
struct timespec ts_clock_gettime;
time_t time_result;
clock_t clock_result;
printf("\n=== 与其他时间函数对比 ===\n");
// gettimeofday
gettimeofday(&tv_gettimeofday, NULL);
// clock_gettime (推荐的现代方法)
clock_gettime(CLOCK_REALTIME, &ts_clock_gettime);
// time
time_result = time(NULL);
// clock
clock_result = clock();
printf("gettimeofday: %ld.%06ld 秒\n",
tv_gettimeofday.tv_sec, tv_gettimeofday.tv_usec);
printf("clock_gettime: %ld.%09ld 秒\n",
ts_clock_gettime.tv_sec, ts_clock_gettime.tv_nsec);
printf("time: %ld 秒\n", time_result);
printf("clock: %ld 时钟滴答\n", clock_result);
// 精度对比
printf("\n精度对比:\n");
printf(" gettimeofday: 微秒级 (%d 纳秒)\n", 1000);
printf(" clock_gettime: 纳秒级 (%d 纳秒)\n", 1);
printf(" time: 秒级 (%d 纳秒)\n", 1000000000);
printf(" clock: 时钟滴答 (%ld 纳秒/滴答)\n",
1000000000L / CLOCKS_PER_SEC);
}
int main() {
precision_test();
performance_benchmark();
comparison_with_other_functions();
return 0;
}
示例3:时间测量和性能分析工具 链接到标题
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <unistd.h>
#include <string.h>
#include <math.h>
typedef struct {
struct timeval start_time;
struct timeval end_time;
int running;
} timer_t;
int timer_start(timer_t *timer) {
if (gettimeofday(&timer->start_time, NULL) == -1) {
return -1;
}
timer->running = 1;
return 0;
}
int timer_stop(timer_t *timer) {
if (!timer->running) {
return -1;
}
if (gettimeofday(&timer->end_time, NULL) == -1) {
return -1;
}
timer->running = 0;
return 0;
}
double timer_elapsed(const timer_t *timer) {
if (timer->running) {
struct timeval current;
gettimeofday(¤t, NULL);
return (current.tv_sec - timer->start_time.tv_sec) +
(current.tv_usec - timer->start_time.tv_usec) / 1000000.0;
} else {
return (timer->end_time.tv_sec - timer->start_time.tv_sec) +
(timer->end_time.tv_usec - timer->start_time.tv_usec) / 1000000.0;
}
}
void timer_print(const timer_t *timer, const char *label) {
double elapsed = timer_elapsed(timer);
printf("%-20s %.6f 秒\n", label, elapsed);
}
// 模拟不同类型的工作
void cpu_intensive_work() {
volatile double sum = 0;
for (int i = 0; i < 10000000; i++) {
sum += sqrt(i);
}
}
void io_intensive_work() {
// 模拟 I/O 操作
for (int i = 0; i < 100; i++) {
usleep(10000); // 10ms
}
}
void mixed_work() {
// 混合工作负载
cpu_intensive_work();
io_intensive_work();
cpu_intensive_work();
}
void performance_analysis() {
timer_t timer;
printf("=== 性能分析测试 ===\n");
// CPU 密集型工作测试
printf("测试 CPU 密集型工作:\n");
if (timer_start(&timer) == 0) {
cpu_intensive_work();
timer_stop(&timer);
timer_print(&timer, " CPU 工作耗时:");
}
// I/O 密集型工作测试
printf("测试 I/O 密集型工作:\n");
if (timer_start(&timer) == 0) {
io_intensive_work();
timer_stop(&timer);
timer_print(&timer, " I/O 工作耗时:");
}
// 混合工作测试
printf("测试混合工作:\n");
if (timer_start(&timer) == 0) {
mixed_work();
timer_stop(&timer);
timer_print(&timer, " 混合工作耗时:");
}
}
void timing_accuracy_test() {
struct timeval tv1, tv2;
double min_diff = 1e30, max_diff = 0, sum_diff = 0;
int samples = 1000;
printf("\n=== 计时精度测试 ===\n");
printf("进行 %d 次连续调用测试...\n", samples);
for (int i = 0; i < samples; i++) {
gettimeofday(&tv1, NULL);
gettimeofday(&tv2, NULL);
double diff = (tv2.tv_sec - tv1.tv_sec) +
(tv2.tv_usec - tv1.tv_usec) / 1000000.0;
if (diff < min_diff) min_diff = diff;
if (diff > max_diff) max_diff = diff;
sum_diff += diff;
}
printf("结果统计:\n");
printf(" 最小时间差: %.9f 秒\n", min_diff);
printf(" 最大时间差: %.9f 秒\n", max_diff);
printf(" 平均时间差: %.9f 秒\n", sum_diff / samples);
printf(" 精度估计: %.0f 纳秒\n", (sum_diff / samples) * 1e9);
}
int main() {
performance_analysis();
timing_accuracy_test();
// 实际应用示例:函数执行时间测量
printf("\n=== 实际应用示例 ===\n");
timer_t execution_timer;
if (timer_start(&execution_timer) == 0) {
printf("执行一些工作...\n");
// 模拟应用程序工作
sleep(1);
usleep(500000); // 0.5秒
timer_stop(&execution_timer);
printf("总执行时间: %.6f 秒\n", timer_elapsed(&execution_timer));
}
return 0;
}
示例4:高精度时间戳和同步测试 链接到标题
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
typedef struct {
struct timeval timestamp;
int thread_id;
int sequence;
} timestamp_record_t;
#define MAX_RECORDS 1000
timestamp_record_t records[MAX_RECORDS];
int record_count = 0;
pthread_mutex_t record_mutex = PTHREAD_MUTEX_INITIALIZER;
void record_timestamp(int thread_id, int sequence) {
timestamp_record_t record;
gettimeofday(&record.timestamp, NULL);
record.thread_id = thread_id;
record.sequence = sequence;
pthread_mutex_lock(&record_mutex);
if (record_count < MAX_RECORDS) {
records[record_count++] = record;
}
pthread_mutex_unlock(&record_mutex);
}
void* timing_thread(void* arg) {
int thread_id = *(int*)arg;
for (int i = 0; i < 100; i++) {
record_timestamp(thread_id, i);
usleep(1000); // 1ms
}
return NULL;
}
void high_precision_timing_test() {
pthread_t threads[3];
int thread_ids[3] = {1, 2, 3};
printf("=== 高精度时间戳测试 ===\n");
printf("创建 3 个线程,每个记录 100 个时间戳...\n");
// 创建线程
for (int i = 0; i < 3; i++) {
if (pthread_create(&threads[i], NULL, timing_thread, &thread_ids[i]) != 0) {
perror("创建线程失败");
return;
}
}
// 等待线程完成
for (int i = 0; i < 3; i++) {
pthread_join(threads[i], NULL);
}
printf("收集到 %d 个时间戳记录\n", record_count);
// 分析时间戳
if (record_count > 0) {
// 按时间排序
for (int i = 0; i < record_count - 1; i++) {
for (int j = i + 1; j < record_count; j++) {
struct timeval *tv1 = &records[i].timestamp;
struct timeval *tv2 = &records[j].timestamp;
if (tv1->tv_sec > tv2->tv_sec ||
(tv1->tv_sec == tv2->tv_sec && tv1->tv_usec > tv2->tv_usec)) {
timestamp_record_t temp = records[i];
records[i] = records[j];
records[j] = temp;
}
}
}
// 显示前 20 个记录
printf("\n前 20 个时间戳记录:\n");
printf("%-5s %-8s %-10s %-15s %-10s\n",
"序号", "线程", "序列", "秒", "微秒");
printf("%-5s %-8s %-10s %-15s %-10s\n",
"----", "----", "----", "----", "----");
for (int i = 0; i < record_count && i < 20; i++) {
printf("%-5d %-8d %-10d %-15ld %-10ld\n",
i + 1,
records[i].thread_id,
records[i].sequence,
records[i].timestamp.tv_sec,
records[i].timestamp.tv_usec);
}
// 计算时间间隔统计
if (record_count > 1) {
double min_interval = 1e30, max_interval = 0, sum_interval = 0;
int interval_count = 0;
for (int i = 1; i < record_count; i++) {
double interval =
(records[i].timestamp.tv_sec - records[i-1].timestamp.tv_sec) +
(records[i].timestamp.tv_usec - records[i-1].timestamp.tv_usec) / 1000000.0;
if (interval >= 0) { // 只统计正向间隔
if (interval < min_interval) min_interval = interval;
if (interval > max_interval) max_interval = interval;
sum_interval += interval;
interval_count++;
}
}
if (interval_count > 0) {
printf("\n时间间隔统计:\n");
printf(" 最小间隔: %.6f 秒 (%.3f 毫秒)\n",
min_interval, min_interval * 1000);
printf(" 最大间隔: %.6f 秒 (%.3f 毫秒)\n",
max_interval, max_interval * 1000);
printf(" 平均间隔: %.6f 秒 (%.3f 毫秒)\n",
sum_interval / interval_count,
(sum_interval / interval_count) * 1000);
}
}
}
}
void monotonicity_test() {
struct timeval prev, current;
int violations = 0;
int samples = 10000;
printf("\n=== 时间单调性测试 ===\n");
printf("进行 %d 次连续调用检查时间单调性...\n", samples);
gettimeofday(&prev, NULL);
for (int i = 0; i < samples; i++) {
gettimeofday(¤t, NULL);
// 检查时间是否倒退
if (current.tv_sec < prev.tv_sec ||
(current.tv_sec == prev.tv_sec && current.tv_usec < prev.tv_usec)) {
printf("时间倒退发现: %ld.%06ld -> %ld.%06ld\n",
prev.tv_sec, prev.tv_usec,
current.tv_sec, current.tv_usec);
violations++;
}
prev = current;
}
printf("时间单调性检查完成:\n");
printf(" 总样本数: %d\n", samples);
printf(" 违反次数: %d\n", violations);
printf(" 正确率: %.2f%%\n", (1.0 - (double)violations / samples) * 100);
}
int main() {
high_precision_timing_test();
monotonicity_test();
return 0;
}
10. 时间格式转换和实用函数 链接到标题
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <time.h>
// 将 timeval 转换为字符串
char* timeval_to_string(const struct timeval *tv, char *buffer, size_t buffer_size) {
struct tm *tm_info = localtime(&tv->tv_sec);
if (tm_info && buffer) {
strftime(buffer, buffer_size, "%Y-%m-%d %H:%M:%S", tm_info);
size_t len = strlen(buffer);
snprintf(buffer + len, buffer_size - len, ".%06ld", tv->tv_usec);
}
return buffer;
}
// 计算两个 timeval 的差值
double timeval_subtract(const struct timeval *x, const struct timeval *y) {
double x_sec = x->tv_sec + x->tv_usec / 1000000.0;
double y_sec = y->tv_sec + y->tv_usec / 1000000.0;
return x_sec - y_sec;
}
// 将秒数转换为 timeval
void seconds_to_timeval(double seconds, struct timeval *tv) {
tv->tv_sec = (time_t)seconds;
tv->tv_usec = (suseconds_t)((seconds - tv->tv_sec) * 1000000);
}
11. 实际应用场景 链接到标题
场景1:性能计时器 链接到标题
typedef struct {
struct timeval start;
struct timeval end;
int is_running;
} performance_timer_t;
void start_timer(performance_timer_t *timer) {
gettimeofday(&timer->start, NULL);
timer->is_running = 1;
}
double stop_timer(performance_timer_t *timer) {
if (!timer->is_running) return -1;
gettimeofday(&timer->end, NULL);
timer->is_running = 0;
return timeval_subtract(&timer->end, &timer->start);
}
场景2:日志时间戳 链接到标题
void log_with_timestamp(const char *message) {
struct timeval tv;
char time_str[64];
gettimeofday(&tv, NULL);
timeval_to_string(&tv, time_str, sizeof(time_str));
printf("[%s] %s\n", time_str, message);
}
场景3:超时控制 链接到标题
int wait_with_timeout(int timeout_ms) {
struct timeval start, current;
gettimeofday(&start, NULL);
while (1) {
// 执行一些操作
gettimeofday(¤t, NULL);
double elapsed = timeval_subtract(¤t, &start);
if (elapsed >= timeout_ms / 1000.0) {
return -1; // 超时
}
if (some_condition_met()) {
return 0; // 成功
}
usleep(1000); // 1ms
}
}
12. 注意事项 链接到标题
使用 gettimeofday
时需要注意:
- 精度限制: 微秒精度,不如
clock_gettime
的纳秒精度 - 时区参数:
tz
参数已废弃,应传入NULL
- 系统时钟: 受系统时钟调整影响(不是单调时钟)
- 性能考虑: 调用开销很小,可以频繁使用
- 线程安全: 是线程安全的
- 移植性: 在所有 Unix-like 系统中都可用
13. 现代替代方案对比 链接到标题
// 推荐的现代方法
void modern_time_getting() {
struct timespec ts;
// 获取实时时间(替代 gettimeofday)
clock_gettime(CLOCK_REALTIME, &ts);
// 获取单调时间(不受系统时钟调整影响)
clock_gettime(CLOCK_MONOTONIC, &ts);
// 更高精度
clock_gettime(CLOCK_REALTIME, &ts);
printf("纳秒精度: %ld.%09ld 秒\n", ts.tv_sec, ts.tv_nsec);
}
总结 链接到标题
gettimeofday
是一个经典且广泛使用的时间获取函数:
主要特点:
- 微秒精度: 提供比
time()
更高精度的时间 - 简单易用: 接口简单,使用方便
- 广泛支持: 在所有 Unix-like 系统中都可用
- 性能良好: 调用开销很小
适用场景:
- 性能计时和基准测试
- 日志记录和调试
- 超时控制和定时器
- 简单的时间戳记录
现代建议:
虽然 gettimeofday
仍然可用且性能良好,但在新代码中建议使用 clock_gettime()
,因为它提供:
- 更高的纳秒精度
- 支持多种时钟源
- 更好的可移植性
- 不受系统时钟调整影响的单调时钟
正确使用 gettimeofday
可以满足大多数时间相关的需求,是系统编程中的重要工具。