强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

POSIX 标准详解教程 / 第十章:时间与定时器

第十章:时间与定时器

掌握 POSIX 时间接口:时钟类型、高精度计时、定时器、休眠函数。


10.1 POSIX 时间概述

10.1.1 时间的基本概念

概念说明
日历时间 (Calendar Time)自 Epoch (1970-01-01 00:00:00 UTC) 以来的秒数
单调时间 (Monotonic Time)单调递增,不受系统时间修改影响
进程时间 (Process Time)进程消耗的 CPU 时间(用户态+内核态)
时钟分辨率时钟的最小精度(纳秒级)

10.1.2 时钟类型

时钟类型说明单调性
实时时钟CLOCK_REALTIME系统日历时间
单调时钟CLOCK_MONOTONIC单调递增(不含休眠时间偏移)
进程 CPU 时钟CLOCK_PROCESS_CPUTIME_ID进程消耗的 CPU 时间
线程 CPU 时钟CLOCK_THREAD_CPUTIME_ID线程消耗的 CPU 时间

关键区别CLOCK_REALTIME 可能被 NTP 或管理员手动修改(跳跃),不适合测量时间间隔。测量时间间隔必须使用 CLOCK_MONOTONIC


10.2 获取时间

10.2.1 clock_gettime()——高精度时间

/*
 * clock_gettime_demo.c - 高精度时间获取
 * 编译: gcc -Wall -o clock_gettime_demo clock_gettime_demo.c
 */
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <time.h>

static void print_timespec(const char *label, struct timespec *ts)
{
    printf("%s: %ld.%09ld 秒\n", label, (long)ts->tv_sec, ts->tv_nsec);
}

int main(void)
{
    struct timespec ts;

    /* 获取各种时钟 */
    clock_gettime(CLOCK_REALTIME, &ts);
    print_timespec("CLOCK_REALTIME", &ts);

    clock_gettime(CLOCK_MONOTONIC, &ts);
    print_timespec("CLOCK_MONOTONIC", &ts);

    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts);
    print_timespec("CLOCK_PROCESS_CPUTIME_ID", &ts);

    clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts);
    print_timespec("CLOCK_THREAD_CPUTIME_ID", &ts);

    /* 查询时钟分辨率 */
    struct timespec res;
    clock_getres(CLOCK_REALTIME, &res);
    print_timespec("CLOCK_REALTIME 分辨率", &res);

    /* 获取日历时间并格式化 */
    struct timespec now;
    clock_gettime(CLOCK_REALTIME, &now);
    struct tm tm;
    localtime_r(&now.tv_sec, &tm);

    char buf[64];
    strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S %Z", &tm);
    printf("当前时间: %s\n", buf);

    return 0;
}
$ ./clock_gettime_demo
CLOCK_REALTIME: 1715289600.123456789 秒
CLOCK_MONOTONIC: 1234567.987654321 秒
CLOCK_PROCESS_CPUTIME_ID: 0.001234567 秒
CLOCK_THREAD_CPUTIME_ID: 0.000987654 秒
CLOCK_REALTIME 分辨率: 0.000000001 秒
当前时间: 2026-05-10 10:00:00 CST

10.2.2 测量代码执行时间

/*
 * time_measure.c - 测量代码块执行时间
 * 编译: gcc -Wall -o time_measure time_measure.c
 */
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <time.h>
#include <string.h>

#define ITERATIONS 1000000

static double timespec_diff_ms(struct timespec *start, struct timespec *end)
{
    double sec = end->tv_sec - start->tv_sec;
    double nsec = end->tv_nsec - start->tv_nsec;
    return sec * 1000.0 + nsec / 1000000.0;
}

int main(void)
{
    struct timespec start, end;
    volatile int sink;

    /* 测量空循环 */
    clock_gettime(CLOCK_MONOTONIC, &start);
    for (int i = 0; i < ITERATIONS; i++)
        sink = i;
    clock_gettime(CLOCK_MONOTONIC, &end);
    printf("空循环 %d 次: %.3f ms\n", ITERATIONS,
           timespec_diff_ms(&start, &end));

    /* 测量 memset */
    char buf[4096];
    clock_gettime(CLOCK_MONOTONIC, &start);
    for (int i = 0; i < ITERATIONS; i++)
        memset(buf, i, sizeof(buf));
    clock_gettime(CLOCK_MONOTONIC, &end);
    printf("memset 4KB %d 次: %.3f ms\n", ITERATIONS,
           timespec_diff_ms(&start, &end));

    (void)sink;
    return 0;
}

10.3 时间格式化

10.3.1 时间转换函数

函数说明线程安全
localtime()秒数 → 本地时间结构
localtime_r()秒数 → 本地时间结构(线程安全)
gmtime()秒数 → UTC 时间结构
gmtime_r()秒数 → UTC 时间结构(线程安全)
mktime()时间结构 → 秒数
strftime()时间结构 → 格式化字符串
strptime()字符串 → 时间结构

10.3.2 格式化示例

/*
 * time_format.c - 时间格式化与解析
 * 编译: gcc -Wall -o time_format time_format.c
 */
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <time.h>
#include <string.h>

int main(void)
{
    struct timespec now;
    clock_gettime(CLOCK_REALTIME, &now);

    struct tm tm_local, tm_utc;
    localtime_r(&now.tv_sec, &tm_local);
    gmtime_r(&now.tv_sec, &tm_utc);

    char buf[128];

    /* 格式化本地时间 */
    strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S %Z", &tm_local);
    printf("本地时间: %s\n", buf);

    /* 格式化 UTC 时间 */
    strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%SZ", &tm_utc);
    printf("UTC 时间: %s\n", buf);

    /* 自定义格式 */
    strftime(buf, sizeof(buf), "%A, %B %d, %Y %I:%M %p", &tm_local);
    printf("自定义格式: %s\n", buf);

    /* 解析时间字符串 */
    struct tm parsed;
    memset(&parsed, 0, sizeof(parsed));
    const char *input = "2026-05-10T15:30:00";
    char *ret = strptime(input, "%Y-%m-%dT%H:%M:%S", &parsed);
    if (ret) {
        strftime(buf, sizeof(buf), "%Y年%m月%d日 %H:%M:%S", &parsed);
        printf("解析 '%s' → %s\n", input, buf);
    }

    /* Unix 时间戳 */
    printf("Unix 时间戳: %ld\n", (long)now.tv_sec);

    return 0;
}

10.4 休眠函数

10.4.1 休眠函数对比

函数精度说明
sleep()休眠指定秒数,可能被信号中断
usleep()微秒已废弃,使用 nanosleep()
nanosleep()纳秒POSIX 标准,纳秒精度休眠
clock_nanosleep()纳秒指定时钟类型,更精确

10.4.2 nanosleep 与 clock_nanosleep

/*
 * sleep_demo.c - POSIX 休眠函数演示
 * 编译: gcc -Wall -o sleep_demo sleep_demo.c
 */
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <time.h>
#include <errno.h>

/* 安全的 nanosleep:处理中断和剩余时间 */
static int safe_nanosleep(const struct timespec *req)
{
    struct timespec rem = *req;
    struct timespec tmp;

    while (nanosleep(&rem, &tmp) == -1) {
        if (errno == EINTR) {
            rem = tmp;  /* 继续休眠剩余时间 */
            continue;
        }
        return -1;
    }
    return 0;
}

int main(void)
{
    struct timespec start, end;

    /* sleep(): 秒级休眠 */
    clock_gettime(CLOCK_MONOTONIC, &start);
    sleep(1);
    clock_gettime(CLOCK_MONOTONIC, &end);
    printf("sleep(1): 实际 %.3f 秒\n",
           (end.tv_sec - start.tv_sec) +
           (end.tv_nsec - start.tv_nsec) / 1e9);

    /* nanosleep(): 毫秒级休眠 */
    clock_gettime(CLOCK_MONOTONIC, &start);
    struct timespec req = { .tv_sec = 0, .tv_nsec = 100000000 };  /* 100ms */
    safe_nanosleep(&req);
    clock_gettime(CLOCK_MONOTONIC, &end);
    printf("nanosleep(100ms): 实际 %.3f ms\n",
           (end.tv_sec - start.tv_sec) * 1000.0 +
           (end.tv_nsec - start.tv_nsec) / 1e6);

    /* clock_nanosleep(): 基于单调时钟的绝对时间 */
    struct timespec abs_time;
    clock_gettime(CLOCK_MONOTONIC, &abs_time);
    abs_time.tv_nsec += 500000000;  /* +500ms */
    if (abs_time.tv_nsec >= 1000000000) {
        abs_time.tv_sec++;
        abs_time.tv_nsec -= 1000000000;
    }
    clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &abs_time, NULL);
    printf("clock_nanosleep(500ms abs) 完成\n");

    return 0;
}

10.5 POSIX 定时器

10.5.1 定时器 API

函数说明
timer_create()创建定时器
timer_settime()启动/修改定时器
timer_gettime()查询定时器剩余时间
timer_delete()删除定时器
timer_getoverrun()获取定时器超时次数

10.5.2 定时器示例

/*
 * posix_timer.c - POSIX 定时器(信号通知方式)
 * 编译: gcc -Wall -o posix_timer posix_timer.c -lrt
 */
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>

static volatile int tick_count = 0;

static void timer_handler(int sig, siginfo_t *si, void *uc)
{
    (void)sig;
    (void)uc;
    (void)si;
    tick_count++;
    const char msg[] = "定时器触发!\n";
    write(STDOUT_FILENO, msg, sizeof(msg) - 1);
}

int main(void)
{
    /* 注册信号处理器 */
    struct sigaction sa = {
        .sa_sigaction = timer_handler,
        .sa_flags = SA_SIGINFO,
    };
    sigemptyset(&sa.sa_mask);
    sigaction(SIGRTMIN, &sa, NULL);

    /* 创建定时器 */
    timer_t timerid;
    struct sigevent sev = {
        .sigev_notify = SIGEV_SIGNAL,
        .sigev_signo = SIGRTMIN,
        .sigev_value.sival_ptr = &timerid,
    };
    if (timer_create(CLOCK_MONOTONIC, &sev, &timerid) == -1) {
        perror("timer_create");
        return 1;
    }

    /* 设置定时器:首次 1 秒后触发,之后每 500ms 重复 */
    struct itimerspec its = {
        .it_value = { .tv_sec = 1, .tv_nsec = 0 },       /* 首次间隔 */
        .it_interval = { .tv_sec = 0, .tv_nsec = 500000000 }, /* 重复间隔 */
    };
    if (timer_settime(timerid, 0, &its, NULL) == -1) {
        perror("timer_settime");
        return 1;
    }

    printf("定时器已启动,等待 5 次触发...\n");

    while (tick_count < 5)
        pause();

    /* 清理 */
    timer_delete(timerid);
    printf("定时器已删除,共触发 %d 次\n", tick_count);

    return 0;
}

10.6 alarm() 与 SIGALRM

/*
 * alarm_demo.c - 使用 alarm() 设置一次性定时器
 * 编译: gcc -Wall -o alarm_demo alarm_demo.c
 */
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <signal.h>
#include <unistd.h>

static volatile sig_atomic_t alarmed = 0;

static void alarm_handler(int sig)
{
    (void)sig;
    alarmed = 1;
}

int main(void)
{
    signal(SIGALRM, alarm_handler);

    printf("设置 3 秒闹钟...\n");
    alarm(3);

    printf("等待中...\n");
    while (!alarmed)
        pause();

    printf("闹钟响了!\n");

    /* alarm(0) 取消未触发的闹钟 */
    printf("设置 10 秒闹钟后立即取消...\n");
    alarm(10);
    unsigned int remaining = alarm(0);
    printf("已取消,原剩余 %u 秒\n", remaining);

    return 0;
}

10.7 进程时间 (Process Times)

/*
 * process_time.c - 获取进程 CPU 使用时间
 * 编译: gcc -Wall -o process_time process_time.c
 */
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <time.h>
#include <sys/times.h>
#include <unistd.h>

static void cpu_intensive_work(void)
{
    volatile long sum = 0;
    for (long i = 0; i < 100000000L; i++)
        sum += i;
    (void)sum;
}

int main(void)
{
    struct tms tms_start, tms_end;
    clock_t real_start, real_end;
    long ticks_per_sec = sysconf(_SC_CLK_TCK);

    real_start = times(&tms_start);

    cpu_intensive_work();

    real_end = times(&tms_end);

    printf("=== 进程时间统计 ===\n");
    printf("实际时间: %.2f 秒\n",
           (double)(real_end - real_start) / ticks_per_sec);
    printf("用户 CPU: %.2f 秒\n",
           (double)(tms_end.tms_utime - tms_start.tms_utime) / ticks_per_sec);
    printf("系统 CPU: %.2f 秒\n",
           (double)(tms_end.tms_stime - tms_start.tms_stime) / ticks_per_sec);
    printf("每秒时钟滴答: %ld\n", ticks_per_sec);

    /* 也可以使用 clock() */
    clock_t c_start = clock();
    cpu_intensive_work();
    clock_t c_end = clock();
    printf("\nclock() 测量: %.3f 秒\n",
           (double)(c_end - c_start) / CLOCKS_PER_SEC);

    return 0;
}

10.8 业务场景:高精度计时器服务

/*
 * timer_wheel.c - 简单定时器管理器
 * 编译: gcc -Wall -o timer_wheel timer_wheel.c -lrt
 */
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>
#include <string.h>
#include <unistd.h>

#define MAX_TIMERS 16

typedef void (*timer_callback_t)(void *arg);

typedef struct {
    timer_t id;
    int active;
    timer_callback_t callback;
    void *arg;
} timer_entry_t;

static timer_entry_t timer_table[MAX_TIMERS];
static volatile sig_atomic_t signaled_idx = -1;

static void timer_handler(int sig, siginfo_t *si, void *uc)
{
    (void)sig;
    (void)uc;
    int idx = (int)(intptr_t)si->si_value.sival_ptr;
    signaled_idx = idx;
}

static int create_timer(int idx, timer_callback_t cb, void *arg,
                         int initial_sec, int interval_sec)
{
    struct sigaction sa = {
        .sa_sigaction = timer_handler,
        .sa_flags = SA_SIGINFO,
    };
    sigemptyset(&sa.sa_mask);
    sigaction(SIGRTMIN, &sa, NULL);

    struct sigevent sev = {
        .sigev_notify = SIGEV_SIGNAL,
        .sigev_signo = SIGRTMIN,
        .sigev_value.sival_ptr = (void *)(intptr_t)idx,
    };

    if (timer_create(CLOCK_MONOTONIC, &sev, &timer_table[idx].id) == -1)
        return -1;

    timer_table[idx].callback = cb;
    timer_table[idx].arg = arg;
    timer_table[idx].active = 1;

    struct itimerspec its = {
        .it_value = { .tv_sec = initial_sec, .tv_nsec = 0 },
        .it_interval = { .tv_sec = interval_sec, .tv_nsec = 0 },
    };
    timer_settime(timer_table[idx].id, 0, &its, NULL);
    return 0;
}

/* 回调函数示例 */
static void heartbeat_cb(void *arg)
{
    (void)arg;
    printf("[心跳] PID=%d\n", getpid());
}

static void stats_cb(void *arg)
{
    (void)arg;
    printf("[统计] 打印运行统计...\n");
}

int main(void)
{
    memset(timer_table, 0, sizeof(timer_table));

    create_timer(0, heartbeat_cb, NULL, 1, 2);  /* 每 2 秒 */
    create_timer(1, stats_cb, NULL, 3, 5);       /* 每 5 秒 */

    printf("定时器服务启动,运行 15 秒...\n");

    for (int i = 0; i < 15; i++) {
        pause();
        if (signaled_idx >= 0 && signaled_idx < MAX_TIMERS &&
            timer_table[signaled_idx].active) {
            timer_table[signaled_idx].callback(
                timer_table[signaled_idx].arg);
            signaled_idx = -1;
        }
    }

    /* 清理 */
    for (int i = 0; i < MAX_TIMERS; i++) {
        if (timer_table[i].active)
            timer_delete(timer_table[i].id);
    }
    printf("定时器服务关闭\n");

    return 0;
}

10.9 注意事项

⚠️ CLOCK_REALTIME 不适合计时:系统时间可能被 NTP 调整。测量时间间隔必须使用 CLOCK_MONOTONIC

⚠️ nanosleep 精度:实际精度取决于内核配置(HZ 值)。Linux 默认精度通常为 1ms-10ms。实时内核(PREEMPT_RT)可获得更高精度。

⚠️ alarm() 与 SIGALRM:每个进程只能有一个 alarm,新 alarm 会覆盖旧的。POSIX 定时器可以创建多个。

⚠️ 定时器信号队列:默认信号队列深度有限。如果定时器触发过快,信号可能丢失。使用实时信号(SIGRTMIN)可排队。

⚠️ timer_delete:定时器不再使用时必须删除,否则持续占用内核资源。


10.10 扩展阅读

  1. man 2 clock_gettimeman 2 nanosleepman 2 timer_create
  2. man 7 time — Linux 时间概述
  3. APUE 第 6 章:System Data Files and Information
  4. TLPI 第 23 章:Timers and Sleeping
  5. RFC 5905 (NTP) — 网络时间协议

10.11 本章小结

要点说明
CLOCK_MONOTONIC测量时间间隔的首选时钟
clock_gettime()纳秒精度获取时间
nanosleep()纳秒精度休眠
POSIX 定时器timer_create/settime,支持信号通知
进程时间times()/clock() 获取 CPU 使用时间
strftime/strptime时间格式化与解析