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: tvtz 指针指向无效内存地址
  • 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(&current, 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(&current, 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(&current, NULL);
        double elapsed = timeval_subtract(&current, &start);
        
        if (elapsed >= timeout_ms / 1000.0) {
            return -1;  // 超时
        }
        
        if (some_condition_met()) {
            return 0;   // 成功
        }
        
        usleep(1000);  // 1ms
    }
}

12. 注意事项 链接到标题

使用 gettimeofday 时需要注意:

  1. 精度限制: 微秒精度,不如 clock_gettime 的纳秒精度
  2. 时区参数: tz 参数已废弃,应传入 NULL
  3. 系统时钟: 受系统时钟调整影响(不是单调时钟)
  4. 性能考虑: 调用开销很小,可以频繁使用
  5. 线程安全: 是线程安全的
  6. 移植性: 在所有 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 是一个经典且广泛使用的时间获取函数:

主要特点:

  1. 微秒精度: 提供比 time() 更高精度的时间
  2. 简单易用: 接口简单,使用方便
  3. 广泛支持: 在所有 Unix-like 系统中都可用
  4. 性能良好: 调用开销很小

适用场景:

  1. 性能计时和基准测试
  2. 日志记录和调试
  3. 超时控制和定时器
  4. 简单的时间戳记录

现代建议: 虽然 gettimeofday 仍然可用且性能良好,但在新代码中建议使用 clock_gettime(),因为它提供:

  • 更高的纳秒精度
  • 支持多种时钟源
  • 更好的可移植性
  • 不受系统时钟调整影响的单调时钟

正确使用 gettimeofday 可以满足大多数时间相关的需求,是系统编程中的重要工具。