首页 > 解决方案 > 如何在 C 中使用 SIGEV_THREAD、sigevent 进行 linux-timer(s) 到期处理?

问题描述

问题:我有计时器正在运行,在计时器到期时需要调用某些函数。输出:Hndlr() 函数内部存在段错误

根据 sigevent 的手册页,它说,

SIGEV_THREAD - 通过调用 sigev_notify_function 来通知进程,“好像”它是一个新线程的启动函数。(这里的实现可能性之一是每个计时器通知都可能导致创建一个新线程,或者创建一个线程来接收所有通知。)

函数 (sigev_notify_function) 以 sigev_value 作为其唯一参数调用

我确实参考了这个:UNIX/Linux 信号处理:SIGEV_THREAD 它说,

sigev_value 包含传递给函数的补充数据

所以,我写了以下内容,

typedef struct Info
{
enum Status
{
    Start = 1,
    Expire = 2
} TimerStatus;

int data;
timer_t timerId;
} Info_t;

void Hndlr(union sigval *sv)
   { 
       //Upon expiry I want to set this value of t1.TimerStatus Expire:
       //t1.TimerStatus = Expire;
       //So I have done this:
       sv->sival_int = Expire;
   }

   int TimerInit(Info_t *Type)
   {
       struct sigevent sev;
       sev.sigev_notify = SIGEV_THREAD;
       sev.sigev_value.sival_ptr = Type->timerId;
       sev.sigev_value.sival_int = Type->TimerStatus;
       sev.sigev_notify_function = &Hndlr;
       sev.sigev_notify_attributes = 0;

       timer_create(CLOCK_REALTIME, &sev, &(Type->timerId));
 }

 //other code

int main(int argc, char const *argv[])
{
Info_t t1;
t1.TimerStatus = Start;

TimerInit(&t1);
//start timer
//other code
while (1)
{

    if (t1.TimerStatus == Expire)
    {
        //do something, invoke a function
    }
}

return 0;

}

Warning: assignment to ‘void (*)(__sigval_t)’ {aka ‘void (*)(union sigval)’} from 
incompatible pointer type ‘void (*)(union sigval *)’ [-Wincompatible-pointer-types]
     sev.sigev_notify_function = &Hndlr; 

因为,我在 Hndlr 中使用 union sigval *sv,所以我收到了这个警告。

Q) 如何将枚举类型作为 pass-by-ptr 传递给 Hndlr 并更改它,即 t1.TimerStatus = Expire

PS:我没有包含涉及 timer_set() 等的整个代码,它还涉及多个计时器实例。那么,我怎样才能实现这个功能(Q)?

标签: clinuxmultithreadingtimerposix

解决方案


代码中的几个错误:

  • 定时器到期函数的函数原型错误Hndlr
  • 设置 的所有成员union sigval,而必须设置一个成员。
  • 在另一个线程中修改和读取的变量必须是原子的。

一个工作示例(编译器选项-std=c11 -pthread -W{all,extra},链接器选项-std=c11 -pthread -lrt):

#include <stdatomic.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>

enum Status {
    Start = 1,
    Expire = 2
};

typedef struct {
    atomic_int status;
    int data;
    timer_t timerId;
} Info_t;

static void Hndlr(union sigval sigev_value) {
    Info_t* info = sigev_value.sival_ptr;
    atomic_store(&info->status, Expire);
}

void TimerInit(Info_t* info, unsigned seconds) {
    int r;
    struct sigevent sev;
    struct itimerspec its;

    sev.sigev_notify = SIGEV_THREAD;
    sev.sigev_value.sival_ptr = info;
    sev.sigev_notify_function = &Hndlr;
    sev.sigev_notify_attributes = 0;
    r = timer_create(CLOCK_REALTIME, &sev, &info->timerId);
    if(r)
        abort();

    its.it_interval.tv_sec = 0;
    its.it_interval.tv_nsec = 0;
    its.it_value.tv_sec = seconds;
    its.it_value.tv_nsec = 0;
    r = timer_settime(info->timerId, 0, &its, NULL);
    if(r)
        abort();
}

int main() {
    Info_t t1;
    t1.status = Start;
    TimerInit(&t1, 3);
    while(atomic_load(&t1.status) != Expire)
        ;

    return 0;
}

在这个特定的用法中,当定时器回调函数只是存储到一个变量中时,不需要使用另一个线程SIGEV_THREADSIGEV_SIGNAL也可以正常工作(需要更改设置代码),只要阻塞可以被中断的函数信号句柄EINTR


推荐阅读