【问题标题】:signalfd() misses signalssignalfd() 错过信号
【发布时间】:2014-09-08 05:06:59
【问题描述】:

在我的程序中,我使用signalfd 来处理信号并将其与poll 结合用于异步IO。以下是我的代码:

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/syscall.h>
#include <sys/signalfd.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <sys/types.h>
#include <poll.h>
#include <assert.h>
#include <errno.h>


volatile sig_atomic_t cont = 1;
volatile sig_atomic_t usrcnt = 0;
volatile sig_atomic_t susrcnt = 0;

volatile sig_atomic_t wsig = 0;
volatile sig_atomic_t wtid = 0;

int GetCurrentThreadId()
{
    return syscall(__NR_gettid);
}

void Segv1(int p1, siginfo_t * p2, void * p3)
{
    //printf("SIGSEGV signal on illegal memory access handled by thread: %d\n", GetCurrentThreadId());
    wtid = GetCurrentThreadId();
    wsig = SIGSEGV;
    _exit(SIGSEGV);
}

void Fpe1(int p1 , siginfo_t * p2, void * p3)
{
    //printf is only for test.
    //printf("FPE signal handled by thread: %d\n", GetCurrentThreadId());
    wtid = GetCurrentThreadId();
    wsig = SIGFPE;
    _exit(SIGFPE);
}

void User1(int p1 , siginfo_t * p2, void * p3)
{
    printf("User signal 1 handled by thread: %d\n", GetCurrentThreadId());
    ++susrcnt;
    wtid = GetCurrentThreadId();
    wsig = SIGUSR1;
}


void* ThreadFunc (void* d)
{

    //Let us use signalfd.
    int sfd;
    sigset_t mask;

    /* We will handle SIGTERM and SIGINT. */
    sigemptyset (&mask);
    sigaddset (&mask, SIGUSR1);

    /* Create a file descriptor from which we will read the signals. */
    sfd = signalfd (-1, &mask, 0);
    if (sfd < 0) {
        printf ("signalfd failed with %d\n", errno);
        return NULL;
    }

    pthread_sigmask(SIG_BLOCK, &mask, NULL);

  /* This is the main loop */
        struct pollfd pfd[1];
        int ret;
        ssize_t bytes;

        pfd[0].fd = sfd;
        pfd[0].events = POLLIN | POLLERR | POLLHUP;

        for (;;) {
                ret = poll(pfd, 1, -1);

                /* Bail on errors (for simplicity) */
                assert(ret > 0);
                assert(pfd[0].revents & POLLIN);

                /* We have a valid signal, read the info from the fd */
                struct signalfd_siginfo info;
                bytes = read(sfd, &info, sizeof(info));
                assert(bytes == sizeof(info));

                unsigned sig = info.ssi_signo;
                unsigned user = info.ssi_uid;

                if (sig == SIGUSR1) {
                    ++usrcnt;
                    printf ("Got SIGUSR1 by POLL in thread: %d: Handler count: %d,  %d\n", GetCurrentThreadId(), susrcnt, usrcnt);
                }
        }

    /* Close the file descriptor if we no longer need it. */
    close (sfd);

    return NULL;
}


int main()
{

    const int numthreads = 1;
    sigset_t sset;
    struct sigaction act;
    int sleepval = 15;
    int pid;
    int i;
        int * a = 0;
    //*a = 1;
    int c=0;
    //c = 0;
    int b;


    printf("My PID: %d\n", getpid());
    printf("SIGSEGV: %d\nSIGFPE: %d\nSIGUSR1: %d\n", SIGSEGV, SIGFPE, SIGUSR1);
    //Create a thread for signal
    memset(&act, 0, sizeof act);
    act.sa_sigaction = User1;
    act.sa_flags    = SA_SIGINFO;

    //Set Handler for SIGUSR1 signal.
    if(sigaction(SIGUSR1, &act, NULL)<0) {
        fprintf(stderr, "sigaction failed\n");
        return 1;
    }


    //Set handler for SIGSEGV signal.
    act.sa_sigaction    = Segv1;
    sigaction(SIGSEGV, &act, NULL);

    //Set handler for SIGFPE (floating point exception) signal.
    act.sa_sigaction    = Fpe1;
    sigaction(SIGFPE, &act, NULL);


    sigemptyset(&sset);
    sigaddset(&sset, SIGUSR1);

    sigprocmask(SIG_UNBLOCK, &sset, NULL);

    pthread_t tid[numthreads];
    for(i=0;i<numthreads;++i)
        pthread_create(&tid[i], NULL, ThreadFunc, NULL);

    //Block the signal for main thread so that other thread handles the the signal.
    pthread_sigmask(SIG_BLOCK, &sset, NULL);

    sleep(numthreads/2);

    //Raise user signal SIGUSR1.
    //raise(SIGUSR1);
    pid = fork();
    if(pid) {
        while(sleepval) {
            sleepval = sleep(sleepval);
            if(sleepval)
                switch(wsig) {
                    case SIGSEGV:
                        printf("[Main] Segmenation fault in thread: %d\n", wtid);
                        exit(1);
                        break;
                    case SIGFPE:
                        printf("[Main] Floating point exception in thread: %d\n", wtid);
                        exit(1);
                        break;
                    case SIGUSR1:
                        printf("[Main] User 1 signal in thread: %d\n", wtid);
                        break;
                    default:
                        printf("[Main] Unhandled signal: %d in thread: %d\n", wsig, wtid);
                        break;
                }
        }

    } else {
         sleep(1); //To avoid race between signal handler and signal fd.

        for(i=0;i<10;++i) {
            //If sleep is not used, signal SIGUSR1 will be handled one time in parent
            //as other signals will be ignored while SIGUSR1 is being handled.
            sleep(1);
            //Problem is here. When the sleep(1) is commented out, it missed the signals.
            kill(getppid(), SIGUSR1);
        }
        return 0;
    }

    return 0;
}

在程序中,进程产生一个创建signalfd 的线程并开始使用poll。然后进程生成一个子进程,它将SIGUSR1 发送到父进程。当信号以 1s 的间隔发送时,它会处理所有的信号。但是,当sleep 被删除时,它会错过通知。

我想知道如果 signalfd 正在处理相同的信号,它是否也会丢弃信号通知。另外,信号处理程序和signalfd之间的优先顺序是什么?

【问题讨论】:

    标签: c linux signals


    【解决方案1】:

    如果多个标准(即:非实时)信号正在等待一个进程,操作系统可能会决定将多个相同类型的信号合并为一个。

    来自POSIX

    2.4.1 信号生成和传递

    [...]

    如果生成了后续出现的未决信号,则在不需要排队的情况下是否多次传递或接受信号是由实现定义的。

    标准信号默认不排队。让标准信号排队的唯一方法是使用sigqueue() 发出它们。

    【讨论】:

    • man 7 signal 可能会提到这一点。稍后我会回来提供详细信息。
    • 谢谢。从我开始使用实时信号的那一刻起,就不会错过信号通知。
    • 我更正了关于使用“queuing”与“pending”的措辞。
    • 感谢sigqueue。但这似乎不起作用。我用sigqueue 替换了kill。有什么遗漏吗?
    【解决方案2】:

    所以这里如果改变信号 > 32 就可以了:

    • 如果信号
    • 如果信号 >= 32 并且也有效,比如这里是 34,将创建一个列表来缓存每个触发器。
      #include <assert.h>
      #include <errno.h>
    
    + #undef SIGUSR1
    + #define SIGUSR1 34
    
      volatile sig_atomic_t cont = 1;
      volatile sig_atomic_t usrcnt = 0;
      volatile sig_atomic_t susrcnt = 0;
    

    以下补丁只是强制刷新 printf:

              int b;
    
    +         setbuf(stdout, NULL);                                                
              printf("My PID: %d\n", getpid());
    

    在 Ubuntu 14.04 上测试,输出:

    My PID: 5249
    SIGSEGV: 11
    SIGFPE: 8
    SIGUSR1: 34
    Got SIGUSR1 by POLL in thread: 5250: Handler count: 0,  1
    Got SIGUSR1 by POLL in thread: 5250: Handler count: 0,  2
    Got SIGUSR1 by POLL in thread: 5250: Handler count: 0,  3
    Got SIGUSR1 by POLL in thread: 5250: Handler count: 0,  4
    Got SIGUSR1 by POLL in thread: 5250: Handler count: 0,  5
    Got SIGUSR1 by POLL in thread: 5250: Handler count: 0,  6
    Got SIGUSR1 by POLL in thread: 5250: Handler count: 0,  7
    Got SIGUSR1 by POLL in thread: 5250: Handler count: 0,  8
    Got SIGUSR1 by POLL in thread: 5250: Handler count: 0,  9
    Got SIGUSR1 by POLL in thread: 5250: Handler count: 0,  10
    

    【讨论】:

      猜你喜欢
      • 2017-08-29
      • 2016-01-21
      • 2013-02-19
      • 2012-04-28
      • 1970-01-01
      • 2015-12-21
      • 1970-01-01
      • 2016-06-16
      • 1970-01-01
      相关资源
      最近更新 更多