signalfd()错过了信号

时间:2014-09-08 05:06:59

标签: c linux signals

在我的程序中,我使用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之间的优先顺序是什么?

2 个答案:

答案 0 :(得分:4)

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

来自POSIX

  

2.4.1信号生成和传递

     

[...]

     

如果后续出现未决信号,则在实施时定义信号是否在需要排队的情况以外的情况下被多次传递或接受。

默认情况下,标准信号不排队。排队标准信号的唯一方法是使用sigqueue()发布它们。

答案 1 :(得分:0)

因此,如果在此处更改信号> 32,则它起作用:

  • 如果信号<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