无阻塞检查循环中的信号

时间:2012-01-09 15:01:43

标签: c linux pthreads signals

我在应用程序中有一个线程,它有一个这样的循环:

...
while (1)
{
    checkDatabase();
    checkChildren();

    sleep(3);
}
...

checkDatabase()不言自明; checkChildren()只需调用waitpid(-1, &status, WNOHANG)来处理已退出或收到信号的子进程。

该应用程序运行良好,但它具有默认信号处理。问题是这个父进程有许多线程(现在不用担心子进程),我没有任何同步信号的经验,更不用说在POSIX线程应用程序中。我之前使用过signal(),但显然它是不可移植的,但它无论如何都不能满足我的要求。我对sigaction方法没有任何经验,我找不到关于如何填写结构等的好文档。

我需要做的是在上面的循环中同步捕获终止信号,如SIGINTSIGTERMSIGQUIT(我需要完全忽略SIGPIPE以便我可以从IO方法中捕获EPIPE错误,所以它看起来像这样:

...
while (1)
{
    checkDatabase();
    checkChildren();
    checkForSignals();

    sleep(3);
}
...

所有其他线程不应与信号有任何关系;只有执行此循环的线程才应该知道它。而且,显然,它需要是非阻塞检查,因此循环在第一次迭代期间不会阻塞。如果找到信号则调用该方法将对其他线程进行排序并销毁互斥锁​​,以及所有这些。

有人可以给我一个单挑吗?非常感谢。

4 个答案:

答案 0 :(得分:3)

(在问题的评论之后,为了完整起见,此解决方案试图避免使用信号处理程序。)

可以通过sigprocmask()(或者更确切地说,pthread_sigmask()来阻止信号被提升,因为你正在使用线程)。从那时起,通过sigpending()可以获得被提升但被阻止的信号。

因此,您可以执行类似的操作(为简洁起见,省略了错误检查):

sigset_t blocked;
sigemptyset(&blocked);
sigaddset(&blocked, SIGINT);
sigaddset(&blocked, SIGTERM);
sigaddset(&blocked, SIGQUIT);
pthread_sigmask(SIG_BLOCK, &blocked, NULL); // Block SIGINT, SIGTERM and SIGQUIT.
signal(SIGPIPE, SIG_IGN);                   // Ignore SIGPIPE.

然后,后来:

void checkForSignals(void)
{
    sigset_t pending;
    sigpending(&pending);
    if (sigismember(&pending, SIGINT)) {
        // Handle SIGINT...
    }
    if (sigismember(&pending, SIGTERM)) {
        // Handle SIGTERM...
    }
    if (sigismember(&pending, SIGQUIT)) {
        // Handle SIGQUIT...
    }
}

由于sigpending()没有阻止,这似乎符合您的要求。

答案 1 :(得分:2)

使用相同的功能为SIGINTSIGTERMSIGQUIT创建信号处理程序。在该信号函数中,只需设置一个可在循环中轮询的标志。

这样的事情:

/* Global variable, will be set to non-zero if SIGINT, SIGTERM or SIGQUIT is caught */
int term_signal_set = 0;

void my_signal_handler(int)
{
    term_signal_set = 1;
}

/* ... */

signal(SIGINT, my_signal_handler);
signal(SIGTERM, my_signal_handler);
signal(SIGQUIT, my_signal_handler);
signal(SIGPIPE, SIG_IGN);  /* So functions return EPIPE */

while (1)
{
    /* ... */

    if (term_signal_set > 0)
        break;  /* Or do something else */

    sleep(3);
}

答案 2 :(得分:1)

在接收信号的多线程应用程序中,没有预先确定,哪个线程接收信号。典型的工作方式包括在信号处理程序中设置全局变量并从专用线程中检查它。

所以在你的情况下,信号处理程序(从任何线程调用)只会为收到的信号设置类似全局变量的东西,而在CheckForSignals()中你会测试它。

答案 3 :(得分:0)

sigaction是要走的路。 man sigaction应该会帮助你。以下是网络上的一个例子

#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <unistd.h> 

struct sigaction act;

void sighandler(int signum, siginfo_t *info, void *ptr)
{
    printf("Received signal %d\n", signum);
    printf("Signal originates from process %lu\n",
        (unsigned long)info->si_pid);
}

int main()
{
    printf("I am %lu\n", (unsigned long)getpid());
        memset(&act, 0, sizeof(act));

    act.sa_sigaction = sighandler;
    act.sa_flags = SA_SIGINFO;

    sigaction(SIGTERM, &act, NULL);
        // Waiting for CTRL+C...
    sleep(100);  
    return 0;
}