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
英文地址,請注明出處:http://en.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三&…