必须指定 CLOCK_MONOTONIC,因其单调递增、不受系统时间调整影响,避免定时器误触发或挂起;CLOCK_REALTIME 会导致 NTP 跳变时行为异常,CLOCK_BOOTTIME 则存在旧内核兼容性问题。

timerfd_create 创建定时器文件描述符时为什么必须指定 CLOCK_MONOTONIC?
因为 timerfd_create 的 clockid 参数若传 CLOCK_REALTIME,系统时间被手动调整(如 NTP 跳变或管理员 date 修改)会导致定时器提前触发或永久挂起;而 CLOCK_MONOTONIC 保证单调递增,不受系统时间修改影响,是 epoll 场景下的唯一可靠选择。
实操建议:
- 始终用
timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC)——TFD_NONBLOCK避免read()阻塞,TFD_CLOEXEC防止 fork 后子进程意外继承 fd - 不要用
CLOCK_BOOTTIME,虽然它也抗挂起,但部分旧内核(如 CentOS 7 默认 kernel 3.10)不支持该 flag,会直接返回 -1 并设errno = EINVAL - 创建失败时检查
errno:常见为ENOSYS(内核未编译 timerfd 支持,极罕见)、EINVAL(clockid 错误)
timerfd_settime 设置相对定时器时 struct itimerspec 的正确填法
很多代码误把 it_value 当成“首次触发延迟”,却忽略它为零时定时器处于停止状态——这是最常导致“timerfd 没反应”的原因。
实操建议:
- 要启动一个 500ms 后首次触发、之后每 1s 触发一次的定时器:
itimerspec.it_value.tv_sec = 0; itimerspec.it_value.tv_nsec = 500000000;itimerspec.it_interval.tv_sec = 1; itimerspec.it_interval.tv_nsec = 0; - 若想立即触发一次(比如做初始化通知),设
it_value.tv_nsec = 1(不能全为 0) - 绝对定时器(
TFD_TIMER_ABSTIME)极少用:需配合clock_gettime(CLOCK_MONOTONIC, &ts)计算绝对时间,且it_value必须大于当前时间,否则行为未定义
epoll_ctl 注册 timerfd 时为什么必须用 EPOLLIN 而非 EPOLLET?
timerfd 的就绪语义和普通 socket 不同:只要定时器到期且未被 read(),它就持续可读;启用 EPOLLET 不仅没收益,反而容易漏事件——因为边缘触发下,一次 epoll_wait() 返回后若没 read() 完,后续再无通知。
实操建议:
- 注册时用
epoll_ctl(epfd, EPOLL_CTL_ADD, tfd, &(struct epoll_event){.events = EPOLLIN, .data.fd = tfd}) - 每次
epoll_wait()返回该 fd 可读,必须调用read(tfd, &exp, sizeof(uint64_t))—— 即使只关心“是否到期”,也要读走这个 8 字节计数器,否则下次epoll_wait()立刻再次返回 -
exp值是自上次read()以来到期次数(通常为 1),用于检测是否发生定时器积压(如系统负载高、处理延迟大)
read(timerfd) 返回 EAGAIN 说明什么?
只有在 timerfd_create 时指定了 TFD_NONBLOCK,且当前无到期事件时,read() 才会返回 -1 并设 errno = EAGAIN。这本身不是错误,而是明确告诉你:“此刻没到期,别等了”。
但实际中几乎不会遇到——因为你在 epoll 中等待它,epoll_wait() 已保证 fd 就绪,此时 read() 必然成功(返回 8)。
真正要注意的是:
- 如果没开
TFD_NONBLOCK,read()会阻塞,直接卡死整个 event loop - 如果
read()返回 0,说明 timerfd 被关闭(极少见,多因重复 close 或 RAII 对象析构顺序错乱) - 如果
read()返回 -1 且errno != EAGAIN(如EBADF),说明 fd 已失效,应从 epoll 中移除并销毁
C++ 封装时最容易被忽略的是:timerfd 的生命周期必须严格绑定到 epoll 实例的生命周期,且 read() 调用不可省略——哪怕你只用它做心跳,漏读一次,下一次 epoll 就立刻又报就绪,形成忙等。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











