signals/t_sigsuspend.c

This is signals/t_sigsuspend.c (Listing 22-5, page 466), an example from the book, The Linux Programming Interface.

The source code file is copyright 2024, Michael Kerrisk, and is licensed under the GNU General Public License, version 3.

This page shows the "distribution" or "book" version of the file (why are there two versions?), or the differences between the two versions. You can switch between the views using the tabs below.

In the listing below, the names of Linux system calls and C library functions are hyperlinked to manual pages from the Linux man-pages project, and the names of functions implemented in the book are hyperlinked to the implementations of those functions.

 

Download signals/t_sigsuspend.c

  Cover of The Linux Programming Interface

Function list (Bold in this list means a function is not static)

/* t_sigsuspend.c

   A short program to demonstrate why sigsuspend(&mask) is preferable to
   calling sigprocmask(SIG_SETMASK, &mask, NULL) + pause() separately.
   (By default this program uses sigsuspend(). To make it use pause(),
   compile using "cc -DUSE_PAUSE".)

   Usage: t_sigsuspend [sleep-time]

   Send the SIGINT signal to this program by typing control-C (^C).
   (Terminate the program using SIGQUIT, i.e., type control-\ (^\).)

   This program contains extra code that does not appear in the version shown
   in the book. By defining USE_PAUSE when compiling, we can replace the use of
   sigsuspend() by the nonatomic sigprocmask() + pause(). This allows us to
   show that doing the latter way will cause some signals to be lost.
*/
#define _GNU_SOURCE     /* Get strsignal() declaration from <string.h> */
#include <string.h>
#include <signal.h>
#include <time.h>
#include "signal_functions.h"           /* Declarations of printSigMask()
                                           and printPendingSigs() */
#include "tlpi_hdr.h"

/* Global variable incremented each time SIGINT is handled */

static volatile int sigintCnt = 0;
static volatile sig_atomic_t gotSigquit = 0;
static void
handler(int sig)
{
    printf("Caught signal %d (%s)\n", sig, strsignal(sig));
                                        /* UNSAFE (see Section 21.1.2) */
    if (sig == SIGQUIT)
        gotSigquit = 1;
    sigintCnt++;
}
int
main(int argc, char *argv[])
{
    int loopNum;
#ifdef USE_PAUSE
    int sleepTime;
#endif
    time_t startTime;
    sigset_t origMask, blockMask;
    struct sigaction sa;

    printSigMask(stdout, "Initial signal mask is:\n");

    sigemptyset(&blockMask);
    sigaddset(&blockMask, SIGINT);
    sigaddset(&blockMask, SIGQUIT);

#ifdef USE_PAUSE
    sleepTime = (argc > 1) ? getInt(argv[1], GN_NONNEG, NULL) : 0;
#endif

    /* Block SIGINT and SIGQUIT - at this point we assume that these signals
      are not already blocked (obviously true in this simple program) so that
      'origMask' will not contain either of these signals after the call. */

    if (sigprocmask(SIG_BLOCK, &blockMask, &origMask) == -1)
        errExit("sigprocmask - SIG_BLOCK");

    /* Set up handlers for SIGINT and SIGQUIT */

    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    sa.sa_handler = handler;
    if (sigaction(SIGINT, &sa, NULL) == -1)
        errExit("sigaction");
    if (sigaction(SIGQUIT, &sa, NULL) == -1)
        errExit("sigaction");

    /* Loop until SIGQUIT received */

    for (loopNum = 1; !gotSigquit; loopNum++) {
        printf("=== LOOP %d\n", loopNum);

        /* Simulate a critical section by delaying a few seconds */

        printSigMask(stdout, "Starting critical section, signal mask is:\n");
        for (startTime = time(NULL); time(NULL) < startTime + 4; )
            continue;                   /* Run for a few seconds elapsed time */

#ifndef USE_PAUSE
        /* The right way: use sigsuspend() to atomically unblock
           signals and pause waiting for signal */

        printPendingSigs(stdout,
                "Before sigsuspend() - pending signals:\n");
        if (sigsuspend(&origMask) == -1 && errno != EINTR)
            errExit("sigsuspend");
#else

        /* The wrong way: unblock signal using sigprocmask(),
           then pause() */

        if (sigprocmask(SIG_SETMASK, &origMask, NULL) == -1)
            errExit("sigprocmask - SIG_SETMASK");

        /* At this point, if SIGINT arrives, it will be caught and
           handled before the pause() call and, in consequence,
           pause() will block. (And thus only another SIGINT signal
           AFTER the pause call() will actually cause the pause()
           call to be interrupted.)  Here we make the window between
           the two calls a bit larger so that we have a better
           chance of sending the signal. */

        if (sleepTime > 0) {
            printf("Unblocked SIGINT, now waiting for %d seconds\n", sleepTime);
            for (startTime = time(NULL);
                    time(NULL) < startTime + sleepTime; )
                continue;
            printf("Finished waiting - now going to pause()\n");
        }

        /* And now wait for the signal */

        pause();

        printf("Signal count = %d\n", sigintCnt);
        sigintCnt = 0;
#endif
    }

    /* Restore signal mask so that signals are unblocked */

    if (sigprocmask(SIG_SETMASK, &origMask, NULL) == -1)
        errExit("sigprocmask - SIG_SETMASK");

    printSigMask(stdout, "=== Exited loop\nRestored signal mask to:\n");

    /* Do other processing... */

    exit(EXIT_SUCCESS);
}

 

Download signals/t_sigsuspend.c

Note that, in most cases, the programs rendered in these web pages are not free standing: you'll typically also need a few other source files (mostly in the lib/ subdirectory) as well. Generally, it's easier to just download the entire source tarball and build the programs with make(1). By hovering your mouse over the various hyperlinked include files and function calls above, you can see which other source files this file depends on.

Valid XHTML 1.1