计时器和信号问题

5
我使用timer_create() API实现了一个POSIX定时器,当定时器到期时会生成SIGUSR1信号,我已经编写了处理程序代码。现在的问题是,如果程序接收到另一个SIGUSR1信号,那么同样的信号处理程序将被调用和捕获。
是否有任何方法可以防止这种情况发生,使处理程序只能捕获由定时器生成的信号?
4个回答

13

这对你是否有用?(从 timer_create 手册示例代码作了修改。)

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <time.h>

#define CLOCKID CLOCK_REALTIME
#define SIG SIGUSR1
timer_t timerid;


static void handler(int sig, siginfo_t *si, void *uc)
{
    if(si->si_value.sival_ptr != &timerid){
        printf("Stray signal\n");
    } else {
        printf("Caught signal %d from timer\n", sig);
    }
}

int main(int argc, char *argv[])
{
    struct sigevent sev;
    struct itimerspec its;
    long long freq_nanosecs;
    sigset_t mask;
    struct sigaction sa;

    printf("Establishing handler for signal %d\n", SIG);
    sa.sa_flags = SA_SIGINFO;
    sa.sa_sigaction = handler;
    sigemptyset(&sa.sa_mask);
    sigaction(SIG, &sa, NULL);

    sev.sigev_notify = SIGEV_SIGNAL;
    sev.sigev_signo = SIG;
    sev.sigev_value.sival_ptr = &timerid;
    timer_create(CLOCKID, &sev, &timerid);
    /* Start the timer */

    its.it_value.tv_sec = 10;
    its.it_value.tv_nsec = 0;
    its.it_interval.tv_sec = its.it_value.tv_sec;
    its.it_interval.tv_nsec = its.it_value.tv_nsec;

    timer_settime(timerid, 0, &its, NULL);
    sleep(100);
    exit(EXIT_SUCCESS);
}

当计时器信号被捕获时,将显示Caught signal 10 from timer。否则将显示Stray signal


2
非常抱歉,我看到你的帖子晚了。你提供了一个很好的解决方案。太棒了!!!还有无数的感谢 :) - RajSanpui
1
+1 非常感谢!原来UNIX中的计时器很简单,只是有点令人困惑。 - Viet
1
如果我需要使用多个计时器来使用这个API怎么办?我尝试过,但在执行处理程序一次后,程序就退出了。有什么办法吗? - mihirj
当然,你只需要将 struct sigaction sa; 改为一个数组(并且你要将所有这些变量重命名为有意义、可维护的名称;-) - Mawg says reinstate Monica

3
问题在于您是否真的需要使用信号。您可以考虑使用回调函数,在定时器到期时调用它:
void cbf(union sigval);
struct sigevent sev;
timer_t timer;

sev.sigev_notify = SIGEV_THREAD;
sev.sigev_notify_function = cbf; //this function will be called when timer expires
sev.sigev_value.sival_ptr = (void*) arg;//this argument will be passed to cbf
timer_create(CLOCK_MONOTONIC, &sev, &timer);

回调函数将在新线程中调用。

不,我们不能使用SIGEV_THREAD,因为我们的库不支持它。此外,SIGEV_THREAD会创建无用的线程,从而导致内存泄漏。 - RajSanpui
系统调用创建的线程会以何种方式导致内存泄漏?你是否已经报告了这个问题? - Mawg says reinstate Monica
虽然我确实喜欢这个解决方案(+1),因为它比使用信号更清晰,但我必须问一下 - 如果在到期之前取消计时器,回调关联是否也会被取消? - Mawg says reinstate Monica

1

不,没有简单的方法。如果您有其他东西与您的计时器一起生成SIGUSR1,请改用SIGUSR2来定时器。如果这不够,请使用一个实时信号来处理您的应用程序。

如果它必须能够处理来自计时器和其他源相同的信号,则取决于速度,数量,系统等等,您可以尝试在注册计时器之前设置时间戳,以确定计时器将大约何时退出,然后在信号处理程序中尝试推断是否在时间间隔内。我强烈建议不要使用此方法,而是重新设计您正在做的事情。


实时信号之一?您能详细说明一下吗?如果测试代码也生成实时信号,那么同一个处理程序会捕获它吗? - RajSanpui
是的,处理器将捕捉信号。实时信号的关键在于它们应该在您自己的应用程序中使用,因为您知道会发生什么。请滚动到手册页上的实时信号并阅读更多信息http://linux.die.net/man/7/signal。 - Milan

1

使用其他RT信号。请参考{{link1:在Linux中是否有创建用户定义信号的方法?}}上的答案。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接