当前位置: 首页 > news >正文

【Linux】timerfd定时器

timerfd 是 Linux 内核提供的一种定时器机制,它通过文件描述符(file descriptor)来通知定时器超时事件,因此可以很方便地融入 selectpollepoll 等 I/O 多路复用机制中,实现高效的定时事件处理。

核心 timerfd API 简介

timerfd 主要涉及三个系统调用:

  1. timerfd_create:创建一个新的定时器对象,并返回对应的文件描述符。

  2. timerfd_settime:启动、停止或设置定时器。

  3. timerfd_gettime:获取定时器的当前设置(可选)。

使用 timerfd 的基本步骤

使用 timerfd 创建和运行一个周期性定时器,通常遵循以下步骤:

  1. 创建定时器 (timerfd_create):

    使用 timerfd_create 函数创建定时器文件描述符。你需要指定时钟源:

    • CLOCK_REALTIME:系统实时时间,可能会因系统时间调整(如 NTP 同步)而受到影响。

    • CLOCK_MONOTONIC:单调递增的时间,从系统启动开始计算,不受系统时间更改的影响,更适合间隔定时。
      flags 参数可以设置为 TFD_NONBLOCK(非阻塞)或 TFD_CLOEXEC,通常可设为 0。

  2. 设置定时器 (timerfd_settime):

    使用 timerfd_settime 函数设置定时器的首次超时时间和周期超时间隔。你需要填充一个 itimerspec 结构体:

    struct itimerspec {struct timespec it_interval;  /* 周期性的超时间隔 */struct timespec it_value;     /* 首次超时时间 */
    };
    • 如果 it_interval 的 tv_sec 和 tv_nsec 都为 0,则定时器只触发一次。

    • 如果 it_value 为 0,则会停止定时器。
      flags 参数可以是 0(相对时间)或 TFD_TIMER_ABSTIME(绝对时间)。

  3. 等待并处理超时事件:
    定时器超时后,其文件描述符会变得可读。你需要使用 read 操作从该文件描述符中读取一个 uint64_t 类型的值,该值表示自上次读取后累积的超时次数。
    非常重要务必在超时后读取该文件描述符,否则后续的超时事件可能无法正常触发(在水平触发模式下,会一直提示可读)。
    你可以使用 read 阻塞等待,或者更常见的,将 timerfd 加入到 selectpollepoll 等 I/O 多路复用的监听集合中,与其他 I/O 事件一起处理。

  4. 关闭定时器:
    当不再需要定时器时,使用 close 函数关闭文件描述符,释放资源。

普通例程

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/timerfd.h>
#include <stdint.h> // 用于 uint64_t
#include <inttypes.h>int main() {// 1. 创建 timerfd,使用单调递增时钟int timer_fd = timerfd_create(CLOCK_MONOTONIC, 0);if (timer_fd == -1) {perror("timerfd_create");exit(EXIT_FAILURE);}// 2. 设置定时器:首次 1 秒后超时,之后每秒超时一次struct itimerspec new_value;new_value.it_value.tv_sec = 1;   // 首次超时:1 秒new_value.it_value.tv_nsec = 0;new_value.it_interval.tv_sec = 1; // 超时间隔:1 秒new_value.it_interval.tv_nsec = 0;if (timerfd_settime(timer_fd, 0, &new_value, NULL) == -1) {perror("timerfd_settime");close(timer_fd);exit(EXIT_FAILURE);}printf("定时器已启动,每秒触发一次...\n");// 3. 循环读取超时事件uint64_t expirations;ssize_t s;while (1) {// read 会阻塞,直到定时器超时s = read(timer_fd, &expirations, sizeof(expirations));if (s != sizeof(expirations)) {perror("read");close(timer_fd);exit(EXIT_FAILURE);}printf("定时器超时次数: %" PRIu64 "\n", expirations);// 在这里执行你的定时任务...}// 4. 关闭 (通常不会执行到这里)close(timer_fd);return 0;
}

应用库


#include <iostream>
#include <functional>
#include <sys/timerfd.h>
#include <unistd.h>
#include <inttypes.h>
#include <thread>
#include <chrono>class TimerFdWrapper
{
public:TimerFdWrapper(int interval, const std::function<void()> &func): interval(interval), func(func), running(false) {}~TimerFdWrapper(){stop();}void start(){if (running)return;running = true;timerThread = std::thread(&TimerFdWrapper::timerLoop, this);}void stop(){if (!running)return;running = false;if (timerThread.joinable())timerThread.join();}
private:int interval;std::function<void()> func;std::thread timerThread;bool running;int initTimerFd(){int fd = timerfd_create(CLOCK_MONOTONIC, 0);if (fd == -1){perror("timerfd_create");return -1;}struct itimerspec its;its.it_value.tv_sec = 0;its.it_value.tv_nsec = interval * 1000000;its.it_interval.tv_sec = 0;its.it_interval.tv_nsec = interval * 1000000;if (timerfd_settime(fd, 0, &its, NULL) == -1){perror("timerfd_settime");close(fd);return -1;}return fd;}void timerLoop(){int fd = initTimerFd();if (fd == -1)return;// 尝试设置新的高优先级,值越大优先级越高(1-99)struct sched_param param;param.sched_priority = 10;pthread_setschedparam(pthread_self(), SCHED_FIFO, &param);uint64_t expirations;while (running){if (read(fd, &expirations, sizeof(expirations)) != sizeof(expirations)){perror("read");break;}// printf("expirations = %d\r\n", expirations);for (uint64_t i = 0; i < expirations; ++i)func();}close(fd);}
};
http://www.dtcms.com/a/350717.html

相关文章:

  • MCP技术详解:探秘模型上下文协议的工作机制与应用
  • 并行多核体系结构基础——概述(笔记)
  • 管理型交换机与非管理型交换机的优缺点对比
  • Coze用户账号设置修改用户昵称-前端源码
  • JavaScript数据表格方案AG Grid主题定制新升级:Figma 设计系统全面打通设计与开发
  • 舰船摇摆下的坐标变换技术
  • coze工作流200+源码,涵盖AI文案生成、图像处理、视频生成、自动化脚本等多个领域
  • Metabase 部署与实践:从测试环境到生产环境的完整指南
  • celery prefetch-multiplier
  • 【人工智能】人工智能在企业中的应用
  • 理解Vuex的辅助函数,分析mapState、mapGetters、mapMutations和mapActions各个应用场景
  • [信号与系统个人笔记]第二章 连续时间信号与系统的时域分析
  • 【MySQL】MySQL介绍及安装
  • 查看mariadb服务是否启动
  • 2025 BrunnerCTF remobile 部分wp
  • 编译型语言(C/C++):从源码到.exe 的完整链路
  • 语音识别:概念与接口
  • LeetCode 面试经典 150_双指针_验证回文串(25_125_C++_简单)(双指针)
  • 【JVM内存结构系列】六、“特殊区域”:直接内存、栈上分配与TLAB
  • JavaScript 对象 Array对象 Math对象
  • Spring Boot 结合 Jasypt 实现敏感信息加密(含 Nacos 配置关联思路)
  • 计算机网络:HTTP、抓包、TCP和UDP报文及重要概念
  • 简述Myisam和Innodb的区别?
  • 面试题:reids缓存和数据库的区别
  • Android FrameWork - Zygote 启动流程分析
  • 【0419】Postgres内核 buffer pool 所需共享内存(shared memory)大小
  • 物流架构实践:ZKmall开源商城物流接口对接与状态同步
  • Pytorch框架的训练测试以及优化
  • 使用JDK11标准 实现 图数据结构的增删查改遍历 可视化程序
  • Spring Cloud Alibaba