Some wrong about sigsetjmp

2019-09-17 05:03发布

问题:

I am reading the APUE, Chapter 10. Here is my code.

#include "apue.h"
#include <unistd.h>
#include <setjmp.h>
#include <time.h>
#include <errno.h>

static void sig_usr1(int), sig_alrm(int);
static sigjmp_buf jmpbuf;
static volatile sig_atomic_t canjmp;

int 
main(void)
{
    if(signal(SIGUSR1, sig_usr1) == SIG_ERR)
        err_sys("signal(SIGUSR1) error");
    if(signal(SIGALRM, sig_alrm) == SIG_ERR)
        err_sys("signal(SIGALRM) error");
    //print signal.
    pr_mask("Starting main: ");
    if(sigsetjmp(jmpbuf, 1)) {
        pr_mask("End main: ");
        exit(0);
    }
    canjmp = 1;
    for(;;)
        pause();
}

static void
sig_usr1(int signo)
{
    time_t starttime;

    if(canjmp == 0) {
        return;
    }

    pr_mask("starting sig_usr1: ");
    alarm(3);
    starttime = time(NULL);
    for(;;) 
        if(time(NULL) > starttime + 5)
            break;
    pr_mask("finishing sig_usr1: ");
    canjmp = 0;
    siglongjmp(jmpbuf, 1);
}

static void
sig_alrm(int signo)
{
    pr_mask("in sig_arlm: ");
}

void
pr_mask(const char *str)
{
    sigset_t    sigset;
    int         errno_save;

    errno_save = errno;     /* we can be called by signal handlers */
    if (sigprocmask(0, NULL, &sigset) < 0)
        err_sys("sigprocmask error");

    printf("%s", str);
    if (sigismember(&sigset, SIGUSR1))  printf("SIGUSR1 ");
    if (sigismember(&sigset, SIGALRM))  printf("SIGALRM ");
    /* remaining signals can go here  */

    printf("\n");
    errno = errno_save;
}

I thought the output would be like this:

Starting main:
starting sig_usr1: SIGUSR1
in sig_alrm: SIGUSR1 SIGALRM
finishing sig_usr1: SIGUSR1
End main:

but it seems something wrong, this is my output in fact:

Starting main:
starting sig_usr1: 
in sig_alrm: 
finishing sig_usr1: 
End main:

that is no signals. Please help me.

回答1:

I think the trouble is probably that you are using signal() and not sigaction() to set the signal handling. And signal() does not mask any other signals - so there are no signals to show as being blocked. I modified your code as shown below, to use signal() and sigaction() depending on whether there are any arguments or not.

#include <signal.h>
#include <unistd.h>
#include <setjmp.h>
#include <time.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

typedef void (*Handler)(int);
static void sig_usr1(int), sig_alrm(int);
static sigjmp_buf jmpbuf;
static volatile sig_atomic_t canjmp;
static void pr_mask(const char *str);

static void err_sys(const char *str)
{
    int errnum = errno;
    fprintf(stderr, "%s (%d: %s)\n", str, errnum, strerror(errnum));
    exit(1);
}

static void set_sigaction(int signum, Handler handler)
{
    struct sigaction nact;
    nact.sa_handler = handler;
    sigfillset(&nact.sa_mask);
    //sigemptyset(&nact.sa_mask);
    nact.sa_flags = 0;
    if (sigaction(signum, &nact, 0) != 0)
        err_sys("Failed to set signal handling");
}

int 
main(int argc, char **argv)
{
    printf("PID = %u\n", (unsigned)getpid());
    if (argc > 1)
    {
        if (signal(SIGUSR1, sig_usr1) == SIG_ERR)
            err_sys("signal(SIGUSR1) error");
        if (signal(SIGALRM, sig_alrm) == SIG_ERR)
            err_sys("signal(SIGALRM) error");
    }
    else
    {
        set_sigaction(SIGUSR1, sig_usr1);
        set_sigaction(SIGALRM, sig_alrm);
    }
    //print signal.
    pr_mask("Starting main: ");
    if (sigsetjmp(jmpbuf, 1)) {
        pr_mask("End main: ");
        exit(0);
    }
    canjmp = 1;
    for (;;)
        pause();
}

static void
sig_usr1(int signo)
{
    time_t starttime;

    if (canjmp == 0) {
        return;
    }

    pr_mask("starting sig_usr1: ");
    alarm(3);
    starttime = time(NULL);
    for (;;) 
        if (time(NULL) > starttime + 5)
            break;
    pr_mask("finishing sig_usr1: ");
    canjmp = 0;
    siglongjmp(jmpbuf, 1);
}

static void
sig_alrm(int signo)
{
    pr_mask("in sig_arlm: ");
}

void
pr_mask(const char *str)
{
    sigset_t    sigset;
    int         errno_save;

    errno_save = errno;     /* we can be called by signal handlers */
    if (sigprocmask(0, NULL, &sigset) < 0)
        err_sys("sigprocmask error");

    printf("%s", str);
    if (sigismember(&sigset, SIGUSR1))  printf("SIGUSR1 ");
    if (sigismember(&sigset, SIGALRM))  printf("SIGALRM ");
    /* remaining signals can go here  */

    printf("\n");
    errno = errno_save;
}

Running on MacOS X 10.7.2 with current XCode (4.2?), I get (for example):

$ ./sigtest
PID = 11066
Starting main: 
starting sig_usr1: SIGUSR1 SIGALRM 
finishing sig_usr1: SIGUSR1 SIGALRM 
in sig_arlm: SIGUSR1 SIGALRM 
End main: 
$ ./sigtest 1
PID = 11067
Starting main: 
starting sig_usr1: SIGUSR1 
in sig_arlm: SIGUSR1 SIGALRM 
finishing sig_usr1: SIGUSR1 
End main: 
$


标签: c linux signals