Linux定时器SIGEV_THREAD使用及回调修改枚举值问题
解决定时器回调段错误与多实例状态修改问题
咱们先拆解下你遇到的两个核心问题:
- 编译警告+段错误:根源是回调函数的签名不匹配——
SIGEV_THREAD要求的回调函数类型是void (*)(union sigval)(传值),但你写的Hndlr是接收union sigval *(传指针),类型不兼容导致警告,运行时把sigval值当成指针解引用,直接触发段错误。 - 无法修改原结构体的
TimerStatus:你试图通过sigval.sival_int传递枚举值,但这是传值,修改的只是副本,根本碰不到t1里的原始值;而且要支持多定时器,得让每个回调能定位到对应的Info_t实例。
修正方案:传递结构体实例指针
正确的做法是把Info_t的实例指针通过sigev_value.sival_ptr传给回调,这样回调就能直接操作原结构体的成员,天然支持多定时器(每个定时器绑定自己的Info_t实例)。
修正后的完整代码
#include <stdio.h> #include <signal.h> #include <time.h> #include <unistd.h> #include <errno.h> typedef struct Info { enum Status { Start = 1, Expire = 2 } TimerStatus; int data; timer_t timerId; } Info_t; // 修正回调函数签名:接收union sigval值,而非指针 void Hndlr(union sigval sv) { // 从sigval中取出Info_t实例的指针 Info_t *info = (Info_t*)sv.sival_ptr; // 直接修改原结构体的TimerStatus info->TimerStatus = Expire; printf("Timer expired! Instance TimerStatus set to Expire\n"); } int TimerInit(Info_t *Type) { struct sigevent sev; sev.sigev_notify = SIGEV_THREAD; // 传递当前Info_t实例的指针,而非单个成员 sev.sigev_value.sival_ptr = Type; // 绑定正确签名的回调函数 sev.sigev_notify_function = Hndlr; sev.sigev_notify_attributes = NULL; // 用NULL更规范 // 检查timer_create的返回值,避免错误 if (timer_create(CLOCK_REALTIME, &sev, &(Type->timerId)) == -1) { perror("timer_create failed"); return -1; } return 0; } int main(int argc, char const *argv[]) { Info_t t1, t2; // 测试多定时器实例 t1.TimerStatus = Start; t1.data = 100; t2.TimerStatus = Start; t2.data = 200; if (TimerInit(&t1) == -1 || TimerInit(&t2) == -1) { return 1; } // 启动定时器:设置1秒后到期,只触发一次 struct itimerspec its; its.it_value.tv_sec = 1; its.it_value.tv_nsec = 0; its.it_interval.tv_sec = 0; its.it_interval.tv_nsec = 0; if (timer_settime(t1.timerId, 0, &its, NULL) == -1) { perror("timer_settime t1 failed"); return 1; } if (timer_settime(t2.timerId, 0, &its, NULL) == -1) { perror("timer_settime t2 failed"); return 1; } // 轮询检查状态 while (1) { if (t1.TimerStatus == Expire) { printf("t1 expired! Data: %d\n", t1.data); t1.TimerStatus = Start; // 重置状态,可再次触发 // 重新启动t1定时器 timer_settime(t1.timerId, 0, &its, NULL); } if (t2.TimerStatus == Expire) { printf("t2 expired! Data: %d\n", t2.data); t2.TimerStatus = Start; timer_settime(t2.timerId, 0, &its, NULL); } sleep(1); } return 0; }
关键修改点说明
- 回调签名修正:把
Hndlr的参数从union sigval *改成union sigval,匹配SIGEV_THREAD要求的函数类型,彻底消除编译警告和段错误。 - 传递实例指针:
sev.sigev_value.sival_ptr = Type把整个Info_t实例的地址传给回调,这样回调能直接修改原结构体的TimerStatus,而不是副本。 - 多实例支持:每个定时器绑定自己的
Info_t实例,回调通过指针区分不同实例,各自修改自己的状态,互不干扰。 - 错误检查:补充了
timer_create和timer_settime的返回值检查,方便排查问题。
额外注意事项
- 如果你的场景中多个定时器回调可能同时修改共享资源(而非各自的实例),需要加互斥锁(
pthread_mutex_t)保证线程安全。 CLOCK_REALTIME是系统实时时钟,如果需要不受系统时间调整影响的定时器,可以改用CLOCK_MONOTONIC。
内容的提问来源于stack exchange,提问作者SatKetchum
相关产品推荐
相关产品推荐

