Linux文件描述符定时器:timerfd系列接口

timerfd 是 Linux 内核 2.6.25 引入的文件描述符式定时器,把定时事件封装为文件描述符的可读事件,可无缝接入 select/poll/epoll 等 IO 多路复用框架,是异步程序(如网络服务)中处理定时任务的常用方案。

timerfd 只有 3 个核心接口,配合 close/read 即可完成所有操作。

1.核心接口

1.1.timerfd_create:创建定时器文件描述符

cpp 复制代码
#include <sys/timerfd.h>
int timerfd_create(int clockid, int flags);

clockid:选择计时时钟类型

cpp 复制代码
CLOCK_REALTIME:系统实时时钟(会被系统时间修改影响)
CLOCK_MONOTONIC:单调递增时钟(不受系统时间修改影响)

flags:扩展标志

cpp 复制代码
TFD_NONBLOCK:将 fd 设置为非阻塞模式
TFD_CLOEXEC:进程执行 exec 时自动关闭该 fd

返回值:成功返回定时器 fd,失败返回 -1(设置 errno)

1.2. timerfd_settime:设置 / 启动定时器

cpp 复制代码
int timerfd_settime(int fd, int flags,
                    const struct itimerspec *new_value,
                    struct itimerspec *old_value);

fd:timerfd_create 返回的定时器 fd。

flags:控制超时时间的解析方式。

cpp 复制代码
0:new_value 中的时间是相对时间(从调用时刻开始计算)
TFD_TIMER_ABSTIME:new_value 中的时间是绝对时间(基于 clockid 对应的时钟)

new_value:定时器超时参数(struct itimerspec 结构)

cpp 复制代码
struct itimerspec {
    struct timespec it_value;    首次超时时间(必须设置,0 表示停止定时器)
    struct timespec it_interval; 周期性超时时间(0 表示只触发一次)
};
struct timespec {
    time_t tv_sec;  秒
    long   tv_nsec; 纳秒(0~999999999)
};

old_value:传出参数,保存定时器的旧配置(不需要则传 NULL)

返回值:成功返回 0,失败返回 -1

1.3. timerfd_gettime:获取定时器剩余时间

cpp 复制代码
int timerfd_gettime(int fd, struct itimerspec *curr_value);

curr_value:传出参数,保存当前定时器的剩余时间(it_value 是剩余时间,it_interval 是周期时间)。

返回值:成功返回 0,失败返回 -1。

2.事件处理-读取溢出次数

当定时器触发时,对应的 fd 会变为可读状态,此时调用 read 可以获取超时溢出次数(即定时器触发但未被处理的次数):

cpp 复制代码
uint64_t overrun;
ssize_t ret = read(fd, &overrun, sizeof(overrun));

read 返回值为 8(因为 uint64_t 占 8 字节),overrun 是溢出次数(正常触发时为 1,若程序处理不及时可能大于 1)。

若 fd 被设为非阻塞模式,未触发时 read 会返回 -1 且 errno=EAGAIN。

3.用 epoll 同时监听网络连接和 timerfd 定时任务

cpp 复制代码
#include <sys/epoll.h>
#include <sys/timerfd.h>
#include <unistd.h>
#include <stdio.h>
#include <stdint.h>

#define MAX_EVENTS 10

int main() {
    // 1. 创建 timerfd
    int tfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
    if (tfd == -1) { perror("timerfd_create"); return 1; }

    // 2. 设置定时器:1秒后首次触发,之后每3秒触发一次
    struct itimerspec its = {
        .it_value = {.tv_sec = 1, .tv_nsec = 0},    // 首次超时
        .it_interval = {.tv_sec = 3, .tv_nsec = 0}  // 周期超时
    };
    if (timerfd_settime(tfd, 0, &its, NULL) == -1) {
        perror("timerfd_settime"); return 1;
    }

    // 3. 创建 epoll 实例,添加 timerfd 到 epoll 监听
    int epollfd = epoll_create1(0);
    struct epoll_event ev, events[MAX_EVENTS];
    ev.events = EPOLLIN;  // 监听可读事件
    ev.data.fd = tfd;
    epoll_ctl(epollfd, EPOLL_CTL_ADD, tfd, &ev);

    // 4. 事件循环
    while (1) {
        int nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
        for (int i = 0; i < nfds; i++) {
            if (events[i].data.fd == tfd) {
                // 处理定时事件:读取溢出次数
                uint64_t overrun;
                read(tfd, &overrun, sizeof(overrun));
                printf("定时器触发,溢出次数:%lu\n", overrun);
            }
            // 可在此处添加网络fd的处理逻辑
        }
    }

    // 5. 清理资源(实际中需在退出时调用)
    close(tfd);
    close(epollfd);
    return 0;
}
相关推荐
树℡独26 分钟前
ns-3仿真之应用层(五)
服务器·网络·tcp/ip·ns3
小小管写大大码1 小时前
如何让vscode变得更智能?vscode接入claude实现自动编程
运维·ide·vscode·自动化·编辑器·ai编程·腾讯云ai代码助手
嵩山小老虎1 小时前
Windows 10/11 安装 WSL2 并配置 VSCode 开发环境(C 语言 / Linux API 适用)
linux·windows·vscode
Fleshy数模1 小时前
CentOS7 安装配置 MySQL5.7 完整教程(本地虚拟机学习版)
linux·mysql·centos
zhang133830890751 小时前
CG-09H 超声波风速风向传感器 加热型 ABS材质 重量轻 没有机械部件
大数据·运维·网络·人工智能·自动化
a41324471 小时前
ubuntu 25 安装vllm
linux·服务器·ubuntu·vllm
Configure-Handler2 小时前
buildroot System configuration
java·服务器·数据库
津津有味道2 小时前
易语言TCP服务端接收刷卡数据并向客户端读卡器发送指令
服务器·网络协议·tcp·易语言
Fᴏʀ ʏ꯭ᴏ꯭ᴜ꯭.3 小时前
Keepalived VIP迁移邮件告警配置指南
运维·服务器·笔记
物联网软硬件开发-轨物科技3 小时前
【轨物洞见】告别“被动维修”!预测性运维如何重塑老旧电站的资产价值?
运维·人工智能