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 扩展阅读
man 2 clock_gettime、man 2 nanosleep、man 2 timer_createman 7 time— Linux 时间概述- APUE 第 6 章:System Data Files and Information
- TLPI 第 23 章:Timers and Sleeping
- RFC 5905 (NTP) — 网络时间协议
10.11 本章小结
| 要点 | 说明 |
|---|---|
| CLOCK_MONOTONIC | 测量时间间隔的首选时钟 |
| clock_gettime() | 纳秒精度获取时间 |
| nanosleep() | 纳秒精度休眠 |
| POSIX 定时器 | timer_create/settime,支持信号通知 |
| 进程时间 | times()/clock() 获取 CPU 使用时间 |
| strftime/strptime | 时间格式化与解析 |