Handle SIGABRT Signal in C++

  1. Use sigaction to Register SIGABRT Signal Handler
  2. Use the sig_atomic_t Variable in Signal Handler

This article will demonstrate multiple methods of how to handle the SIGABRT signal in C++.

Use sigaction to Register SIGABRT Signal Handler

Unix-based operating systems support the feature called signal, a mechanism by which one program can asynchronously send a message to another program. Signals are usually sent from the user or the operating system processes that need to interrupt the program. Signal handler is the code section that gets called if the program receives the given signal. There are some standard signals for tasks like aborting the program, stopping the program, and continuing the program with their corresponding default signal handlers. However, the user can override most of these signal handlers with custom-defined functions, and sigaction is the function that registers it.

Note that SIGABRT is one of the signals which results in program termination by default. In the next code example, we override its signal handler and assign our defined function(cleanupRoutine) to be called once the signal is received.

At first, struct sigaction type object should be declared and initialized with the memset function call.

Next, its data member sa_handler should be assigned the address of the function that needs to be called.

After this, we need to mask other signals from interrupting the SIGABRT handler, and the sigfillset function call achieves that.

Finally, we register the signal handler with sigaction call that takes three arguments: signal number, address of the struct sigaction, and an optional struct where the previous action can be saved.

In this case, we ignore the third argument, but the nullptr still needs to be specified as a parameter. We defined the cleanupRoutine function to print the string to the console and then exit the program to verify the handler’s correct execution easily. The rest of the program is the infinite loop that needs to be interrupted when the user sends the SIGABRT signal. To test the program, execute it in one terminal window and send the signal from the second window by executing the following command kill -SIGABRT pid_num_of_program.

#include <iostream>
#include <cstring>
#include <csignal>

void cleanupRoutine(int signal_number)
{
    write(2, "printed from cleanupRoutine\n", 28);
    _exit(EXIT_SUCCESS);
}

int main()
{
    struct sigaction sigabrt_action{};
    memset(&sigabrt_action, 0, sizeof(sigabrt_action));
    sigabrt_action.sa_handler = &cleanupRoutine;

    if (sigfillset(&sigabrt_action.sa_mask) != 0)
    {
        perror("sigfillset");
        exit(EXIT_FAILURE);
    }
    if (sigaction(SIGABRT, &sigabrt_action, nullptr) != 0)
    {
        perror("sigaction SIGABRT");
        exit(EXIT_FAILURE);
    }

    int i = 0;
    while (true)
    {
        i += 1;
    }

    exit(EXIT_SUCCESS);
}

Use the sig_atomic_t Variable in Signal Handler

Signal handlers are special types of functions that are required to have certain features. Namely, the only variables guaranteed to be correctly operated on are atomic variables. There is a special type sig_atomic_t, an integer that can be set during the signal handler execution. This variable is declared with the volatile keyword, and any modification to it is seen globally. The following example demonstrates how can we include this variable as a condition to the loop statement.

Notice that the handler only sets the variable from 1 to 0 and not exiting the program if the signal is received. Meaning, the program tries to continue from the point where it was interrupted. In this case, loop iteration is restarted, and when once the condition is checked to be false it jumps out of the loop. This way, the user can control the program behavior using the signals.

#include <iostream>
#include <cstring>
#include <csignal>

using std::cout; using std::endl;

volatile sig_atomic_t shutdown_flag = 1;

void cleanupRoutine(int signal_number)
{
    shutdown_flag = 0;
}

int main()
{
    struct sigaction sigabrt_action{};
    memset(&sigabrt_action, 0, sizeof(sigabrt_action));
    sigabrt_action.sa_handler = &cleanupRoutine;

    if (sigfillset(&sigabrt_action.sa_mask) != 0)
    {
        perror("sigfillset");
        exit(EXIT_FAILURE);
    }
    if (sigaction(SIGABRT, &sigabrt_action, nullptr) != 0)
    {
        perror("sigaction SIGABRT");
        exit(EXIT_FAILURE);
    }


    int i = 0;
    while (shutdown_flag)
    {
        i += 1;
    }
    cout << "Exiting ..." << endl;

    exit(EXIT_SUCCESS);
}