Linux timerfd 定时器封装

使用 timerfd + epoll() 实现,简洁精确。

没定义 MU_ERROR 宏的话替换为 printf 即可。

mu_timer.h:

#ifndef _MU_TIMER_H_
#define _MU_TIMER_H_#ifdef __cplusplus
extern "C"
{
#endif#include <stdint.h>
#include <time.h>
#include <pthread.h>/*** @brief 定时器状态*/typedef enum{MU_TIMER_UNINITIALIZED,MU_TIMER_RUNNING,MU_TIMER_PAUSED} TimerStatus;/*** @brief 定时器回调类型*/typedef void (*MuTimerCallback)(void *arg);/*** @brief 定时器结构体*/typedef struct{int timerfd;              ///< timerfd文件描述符struct itimerspec tmr;    ///< 时间间隔TimerStatus status;       ///< 当前状态int initialized;          ///< 是否初始化int counter;              ///< 剩余触发次数(-1为无限次)MuTimerCallback callback; ///< 回调函数void *arg;                ///< 回调参数} MuTimer;/*** @brief 初始化定时器管理器** @return int 0成功,-1失败*/int mu_timer_init(void);/*** @brief 去初始化定时器管理器** @return int 0成功,-1失败*/int mu_timer_deinit(void);/*** @brief 创建并启动定时器** @param timer 定时器指针* @param interval 时间间隔* @param callback 回调* @param arg 回调参数* @param trigger_count 触发次数(-1为无限次)* @return int 0成功,-1失败* @note* 回调函数应尽量简洁,避免执行耗时操作,以免影响其他定时器的运行。* 当定时器达到设定的触发次数后,会自动销毁,无需手动调用 mu_timer_destroy*/int mu_timer_create(MuTimer *timer, struct timespec interval, MuTimerCallback callback,void *arg, int trigger_count);/*** @brief 暂停定时器** @param timer 定时器指针* @return int 0成功,-1失败*/int mu_timer_pause(MuTimer *timer);/*** @brief 恢复定时器** @param timer 定时器指针* @return int 0成功,-1失败*/int mu_timer_resume(MuTimer *timer);/*** @brief 重置定时器时间间隔** @param timer 定时器指针* @return 0成功,-1失败* @note 若定时器暂停,需恢复后才会计时*/int mu_timer_reset(MuTimer *timer);/*** @brief 设置定时器时间间隔** @param timer 定时器指针* @param interval 时间间隔* @return int 0成功,-1失败* @note 若定时器暂停,需恢复后才会计时*/int mu_timer_set_interval(MuTimer *timer, struct timespec interval);/*** @brief 销毁定时器** @param timer 定时器指针* @return int 0成功,-1失败*/int mu_timer_destroy(MuTimer *timer);#ifdef __cplusplus
}
#endif#endif // _MU_TIMER_H_

mu_timer.c:

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <sys/timerfd.h>
#include <sys/epoll.h>
#include <stdint.h>#include "mu_timer.h"
#include "mu_debug.h"#define MAX_EPOLL_EVENTS 32 ///< epoll一次最多处理的事件数// 定时器管理器结构体
typedef struct
{int epfd;               // epoll文件描述符pthread_t event_thread; // 事件处理线程pthread_mutex_t mutex;  // 管理器互斥锁int running;            // 运行状态int initialized;        // 初始化标记
} MuTimerManager;// 全局管理器实例
static MuTimerManager g_timer_manager = {0};// 规范化timespec
static void normalize_interval(struct timespec *interval)
{if (interval->tv_nsec >= 1000000000L){interval->tv_sec += interval->tv_nsec / 1000000000L;interval->tv_nsec %= 1000000000L;}
}// 处理定时器到期事件
static void handle_timer_expired(MuTimer *timer)
{pthread_mutex_lock(&g_timer_manager.mutex);if (!timer || !timer->initialized || timer->status != MU_TIMER_RUNNING){pthread_mutex_unlock(&g_timer_manager.mutex);return;}// 读取timerfd,清除可读状态uint64_t exp;ssize_t s = read(timer->timerfd, &exp, sizeof(uint64_t));if (s != sizeof(uint64_t)){MU_ERROR("read timerfd failed: %s", strerror(errno));{pthread_mutex_unlock(&g_timer_manager.mutex);return;}}// 更新计数器if (timer->counter > 0)timer->counter--;// 执行回调if (timer->callback){pthread_mutex_unlock(&g_timer_manager.mutex);timer->callback(timer->arg);pthread_mutex_lock(&g_timer_manager.mutex);}// 检查是否需要销毁定时器if (timer->counter == 0){pthread_mutex_unlock(&g_timer_manager.mutex);mu_timer_destroy(timer);pthread_mutex_lock(&g_timer_manager.mutex);}pthread_mutex_unlock(&g_timer_manager.mutex);
}// 事件处理线程
static void *timer_event_thread(void *arg)
{struct epoll_event events[MAX_EPOLL_EVENTS];while (g_timer_manager.running){int nfds = epoll_wait(g_timer_manager.epfd, events, MAX_EPOLL_EVENTS, 1000); // 1秒超时if (nfds == -1){if (errno == EINTR){continue;}MU_ERROR("epoll_wait failed: %s", strerror(errno));break;}for (int i = 0; i < nfds; i++){if (events[i].events & EPOLLIN){MuTimer *timer = (MuTimer *)events[i].data.ptr;handle_timer_expired(timer);}}}return NULL;
}// 初始化定时器管理器
int mu_timer_init(void)
{if (g_timer_manager.initialized){return -1;}// 创建epoll实例g_timer_manager.epfd = epoll_create1(EPOLL_CLOEXEC);if (g_timer_manager.epfd == -1){MU_ERROR("epoll_create1 failed: %s", strerror(errno));return -1;}// 创建事件处理线程if (pthread_create(&g_timer_manager.event_thread, NULL, timer_event_thread, NULL) != 0){MU_ERROR("pthread_create failed: %s", strerror(errno));close(g_timer_manager.epfd);g_timer_manager.epfd = 0;return -1;}pthread_mutex_init(&g_timer_manager.mutex, NULL);g_timer_manager.running = 1;g_timer_manager.initialized = 1;return 0;
}// 去初始化定时器管理器
int mu_timer_deinit(void)
{if (!g_timer_manager.initialized){return -1;}g_timer_manager.running = 0;// 等待事件处理线程结束pthread_join(g_timer_manager.event_thread, NULL);if (g_timer_manager.epfd > 0){close(g_timer_manager.epfd);}pthread_mutex_destroy(&g_timer_manager.mutex);memset(&g_timer_manager, 0, sizeof(g_timer_manager));return 0;
}int mu_timer_create(MuTimer *timer, struct timespec interval, MuTimerCallback callback,void *arg, int trigger_count)
{if (!timer || !callback || trigger_count == 0 ||interval.tv_sec < 0 || interval.tv_nsec < 0 ||(interval.tv_sec == 0 && interval.tv_nsec == 0) ||!g_timer_manager.initialized)return -1;memset(timer, 0, sizeof(MuTimer));// 创建timerfdtimer->timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);if (timer->timerfd == -1){MU_ERROR("timerfd_create failed: %s", strerror(errno));return -1;}normalize_interval(&interval);// 设置timerfdtimer->tmr.it_interval = interval;timer->tmr.it_value = interval;if (timerfd_settime(timer->timerfd, 0, &timer->tmr, NULL) == -1){MU_ERROR("timerfd_settime failed: %s", strerror(errno));close(timer->timerfd);return -1;}timer->callback = callback;timer->arg = arg;timer->counter = trigger_count;timer->status = MU_TIMER_RUNNING;// 添加到epoll监听struct epoll_event ev;ev.events = EPOLLIN;ev.data.ptr = timer;if (epoll_ctl(g_timer_manager.epfd, EPOLL_CTL_ADD, timer->timerfd, &ev) == -1){MU_ERROR("epoll_ctl ADD failed: %s", strerror(errno));close(timer->timerfd);return -1;}timer->initialized = 1;return 0;
}int mu_timer_pause(MuTimer *timer)
{int ret = 0;if (!g_timer_manager.initialized)return -1;pthread_mutex_lock(&g_timer_manager.mutex);if (!timer || !timer->initialized){ret = -1;goto exit;}if (timer->status != MU_TIMER_RUNNING)goto exit;// 获取剩余时间if (timerfd_gettime(timer->timerfd, &timer->tmr) == -1){MU_ERROR("timerfd_gettime failed: %s", strerror(errno));ret = -1;goto exit;}// 停止timerfdstruct itimerspec its;memset(&its, 0, sizeof(its));if (timerfd_settime(timer->timerfd, 0, &its, NULL) == -1){MU_ERROR("timerfd_settime failed: %s", strerror(errno));ret = -1;goto exit;}timer->status = MU_TIMER_PAUSED;exit:pthread_mutex_unlock(&g_timer_manager.mutex);return ret;
}int mu_timer_resume(MuTimer *timer)
{int ret = 0;if (!g_timer_manager.initialized)return -1;pthread_mutex_lock(&g_timer_manager.mutex);if (!timer || !timer->initialized){ret = -1;goto exit;}if (timer->status != MU_TIMER_PAUSED || timer->counter == 0)goto exit;// 重新启动timerfdif (timerfd_settime(timer->timerfd, 0, &timer->tmr, NULL) == -1){MU_ERROR("timerfd_settime failed: %s", strerror(errno));ret = -1;goto exit;}timer->status = MU_TIMER_RUNNING;exit:pthread_mutex_unlock(&g_timer_manager.mutex);return ret;
}int mu_timer_reset(MuTimer *timer)
{int ret = 0;if (!g_timer_manager.initialized)return -1;pthread_mutex_lock(&g_timer_manager.mutex);if (!timer || !timer->initialized){ret = -1;goto exit;}// 重置超时时间timer->tmr.it_value = timer->tmr.it_interval;if (timer->status == MU_TIMER_RUNNING){if (timerfd_settime(timer->timerfd, 0, &timer->tmr, NULL) == -1){MU_ERROR("timerfd_settime failed: %s", strerror(errno));ret = -1;goto exit;}}exit:pthread_mutex_unlock(&g_timer_manager.mutex);return ret;
}int mu_timer_set_interval(MuTimer *timer, struct timespec interval)
{int ret = 0;if (!g_timer_manager.initialized || interval.tv_sec < 0 || interval.tv_nsec < 0 ||(interval.tv_sec == 0 && interval.tv_nsec == 0))return -1;pthread_mutex_lock(&g_timer_manager.mutex);if (!timer || !timer->initialized){ret = -1;goto exit;}normalize_interval(&interval);// 重新设置间隔timer->tmr.it_interval = interval;timer->tmr.it_value = interval;if (timer->status == MU_TIMER_RUNNING){if (timerfd_settime(timer->timerfd, 0, &timer->tmr, NULL) == -1){MU_ERROR("timerfd_settime failed: %s", strerror(errno));ret = -1;goto exit;}}exit:pthread_mutex_unlock(&g_timer_manager.mutex);return ret;
}int mu_timer_destroy(MuTimer *timer)
{int ret = 0;if (!g_timer_manager.initialized)return -1;pthread_mutex_lock(&g_timer_manager.mutex);if (!timer || !timer->initialized){ret = -1;goto exit;}// 从epoll中移除if (epoll_ctl(g_timer_manager.epfd, EPOLL_CTL_DEL, timer->timerfd, NULL) == -1){MU_ERROR("epoll_ctl DEL failed: %s", strerror(errno));ret = -1;}close(timer->timerfd);memset(timer, 0, sizeof(MuTimer));exit:pthread_mutex_unlock(&g_timer_manager.mutex);return ret;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/89916.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/89916.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【样式效果】Vue3实现仿制iOS按钮动态效果

iOS开关效果定义变量&#xff1a; <style scoped lang"scss">.layout {// 按钮宽度$button-width: 500px;// 按钮高度$button-height: 250px;// 按钮里面圆形直径$circle-diameter: 200px;// 按钮背景与里面圆形间距$button-circle-offset:calc(($button-he…

京东疯狂投资具身智能:众擎机器人+千寻智能+逐际动力 | AI早报

每日分享全球最新AI资讯【应用商业八卦技术】&#xff0c;&#x1f30f;&#xff1a;未来世界2099应用 1、马斯克推出儿童AI"Baby Grok"引热议&#xff1a;安全性能否经受考验&#xff1f; 2、蚂蚁AQ健康应用霸榜苹果商店&#xff0c;或将联手Apple Watch打造智能健康…

Jiasou TideFlow AIGC SEO Agent:全自动外链构建技术重构智能营销新标准

AI时代SEO技术革命&#xff1a;企业如何突破流量增长瓶颈&#xff1f;随着Google算法升级至MUM模型&#xff0c;传统SEO工具已难以应对多模态内容优化需求。在搜索引擎日均处理120亿次查询的生态中&#xff0c;企业官网平均自然流量转化周期长达6-8个月&#xff0c;因此诸如Jia…

Docker-compose:服务编排

Docker-compose 介绍 服务编排:按照一定的业务规则批量管理容器 在微服务架构的应用系统中,一般包含 N 个微服务,且每个微服务一般都会部署多个实例。此时,如果每个微服务都要手动启停,维护的工作量会很大。 要从 Dockerfile build image 或者去 docker hub 拉取 image …

异地服务器备份Mysql数据

前言异地服务器备份Mysql数据即Mysql的server端与备份服务器不是同一个。Mysql服务端安装在192.168.3.36中&#xff0c;现在需要在IP为192.168.209.129的服务器中使用mysqldump命令备份指定数据库数据;192.168.209.129没有装过Mysql客户端;1.安装Mysql客户端不安装Mysql客户端就…

NGINX 高级配置解析:`proxy_request_buffering` 使用详解

在使用 NGINX 作为反向代理服务器时&#xff0c;处理客户端请求体&#xff08;如上传文件或大体积 POST 请求&#xff09;的方式会直接影响应用性能与资源使用。其中&#xff0c;proxy_request_buffering 是一个非常关键但容易被忽略的配置项。 本文将详细介绍该指令的作用、典…

增加交叉验证和超参数调优

前文中&#xff0c;只是给了基础模型&#xff1a; PyTorch 实现 CIFAR-10 图像分类&#xff1a;从数据预处理到模型训练与评估-CSDN博客 今天我们增加交叉验证和超参数调优&#xff0c; 先看运行结果&#xff1a; 在测试集上评估最终模型 最终模型在测试集上的准确率&…

解决pip指令超时问题

用pip指令&#xff0c;在安装Django3.2时报错&#xff0c;询问ChatGpt后得到的解决方案pip 下载超时 —— 是 当前网络连接到 PyPI 官方源太慢或不稳定&#xff0c;甚至可能连不上了&#xff0c;而 pip 默认的超时时间又太短&#xff0c;就导致了中途失败&#xff1a;ReadTimeo…

Oracle定时清理归档日志

线上归档日志满了&#xff0c;系统直接崩了&#xff0c;为解决这个问题&#xff0c;创建每月定时清理归档日志。 创建文件名 delete_archivelog.rman CONFIGURE ARCHIVELOG DELETION POLICY CLEAR; RUN {ALLOCATE CHANNEL c1 TYPE DISK;DELETE ARCHIVELOG ALL COMPLETED BEFORE…

ELF 文件操作手册

目录 一、ELF 文件结构概述 二、查看 ELF 文件头信息 1、命令选项 2、示例输出 3、内核数据结构 三、ELF 程序头表 1、命令选项 2、示例输出 3、关键说明 4、内核数据结构 四、ELF 节头表详解 查看节头表信息 1、命令选项 2、示例输出 3、标志说明 4、重要节说…

深入浅出Python函数:参数传递、作用域与案例详解

&#x1f64b;‍♀️ 博主介绍&#xff1a;颜颜yan_ ⭐ 本期精彩&#xff1a;深入浅出Python函数&#xff1a;参数传递、作用域与案例详解 &#x1f3c6; 热门专栏&#xff1a;零基础玩转Python爬虫&#xff1a;手把手教你成为数据猎人 &#x1f680; 专栏亮点&#xff1a;零基…

ps aux 和 ps -ef

在 Linux/Unix 系统中&#xff0c;ps aux 和 ps -ef 都是用于查看进程信息的命令&#xff0c;结合 grep node 可以筛选出与 Node.js 相关的进程。它们的核心功能相似&#xff0c;但在输出格式和选项含义上有区别&#xff1a;1. 命令对比命令含义主要区别ps auxBSD 风格语法列更…

Spark ML 之 LSH

src/test/scala/org/apache/spark/ml/feature/BucketedRandomProjectionLSHSuite.scala test("approxSimilarityJoin for self join") {val data = {for (i <- 0 until 24) yield Vectors

关键成功因素法(CSF)深度解析:从战略目标到数据字典

关键成功因素法由John Rockart提出&#xff0c;用于信息系统规划&#xff0c;帮助企业识别影响系统成功的关键因素&#xff0c;从而确定信息需求&#xff0c;指导信息技术管理。该方法通过识别关键成功因素&#xff0c;找出关键信息集合&#xff0c;确定系统开发优先级&#xf…

Django母婴商城项目实践(六)- Models模型之ORM操作

6、Models模型操作 1 ORM概述 介绍 Django对数据进行增删改操作是借助内置的ORM框架(Object Relational Mapping,对象关系映射)所提供的API方法实现的,允许你使用类和对象对数据库进行操作,从而避免通过SQL语句操作数据库。 简单来说,ORM框架的数据操作API是在 QuerySet…

【PTA数据结构 | C语言版】哥尼斯堡的“七桥问题”

本专栏持续输出数据结构题目集&#xff0c;欢迎订阅。 文章目录题目代码题目 哥尼斯堡是位于普累格河上的一座城市&#xff0c;它包含两个岛屿及连接它们的七座桥&#xff0c;如下图所示。 可否走过这样的七座桥&#xff0c;而且每桥只走过一次&#xff1f;瑞士数学家欧拉(Leo…

Redis 详解:从入门到进阶

文章目录前言一、什么是 Redis&#xff1f;二、Redis 使用场景1. 缓存热点数据2. 消息队列3. 分布式锁4. 限流与防刷5. 计数器、排行榜三、缓存三大问题&#xff1a;雪崩 / 穿透 / 击穿1. ❄️ 缓存雪崩&#xff08;Cache Avalanche&#xff09;2. &#x1f50d; 缓存穿透&…

QCustomPlot 使用教程

下载网址&#xff1a;官方网站&#xff1a;http://www.qcustomplot.com/我的环境是 window10 qt5.9.9 下载后&#xff0c;官网提供了很多例子。可以作为参考直接运行自己如何使用&#xff1a;第一步&#xff1a;使用QCustomPlot非常简单&#xff0c;只需要把qcustomplot.cpp和…

基于springboot+mysql的作业管理系统(源码+论文)

一、开发环境 1 Spring Boot框架简介 描述&#xff1a; 简化开发&#xff1a;Spring Boot旨在简化新Spring应用的初始搭建和开发过程。配置方式&#xff1a;采用特定的配置方式&#xff0c;减少样板化配置&#xff0c;使开发人员无需定义繁琐的配置。开发工具&#xff1a;可…

LVS 集群技术基础

LVS(linux virual server)LVS集群技术---NAT模式一.准备四台虚拟机1.client(eth0ip:172.254.100)2.lvs(eth0ip:172.254.200;eth1ip:192.168.0.200)3.rs1(eht0ip:192.168.0.10)4.rs2(eth0ip:192.168.0.20)二&#xff1a;在rs1和rs2安装httpd功能dnf/yum install htppd -y三&…