C++ Signal Handling

In C++, signals are the interrupts, which are delivered to a process by the operating system to stop its ongoing task and attend to the task for which the interrupt has been generated. Interrupts are used to notify the program of specific events or conditions, such as an error, a termination request, or an external input. When a signal is received, the operating system stops the program's usual execution to handle it.

In C++, signals can also be generated by the operating system on the basis of system or error conditions. We can generate interrupts by pressing Ctrl+ C on Linux , UNIX, Mac OS X, or Windows system.

When a signal is given, the system can:

  • Terminate the program.
  • Pause it
  • Just ignore it.
  • Alternatively, invoke a programmer-defined custom handler function.
  • Catching and Handling Signals

In C++ , not all signals can be caught or handled. Some signals are non-catchable, such as SIGKILL or SIGSTOP, and the operating system strictly enforces them to ensure system stability. Many other signals, such as SIGINT, SIGFPE, SIGSEGV, and SIGTERM, can be intercepted and handled within a program.

If we want to work with signals in C++, utilize the <csingnal> header, which provides signal names and functions, such as signal to associate a handler with a signal.

C++ Simple Signal Handling Example:

Let us take an example to illustrate the signal handling in C++.

Example

Example

#include <iostream>

using namespace std;   //using standard namespace

int main()   //main function

{

    while (true) 

    {

        cout << "Hello, World!!\n";

    }

    return 0;

}

Output:

Output

Hello, World!!

Hello, World!!

Hello, World!!

Hello, World!!

Hello, World!!

Explanation:

In this example, we create an infinite loop that writes the message "Hello, World!!" to the terminal. When the user presses Ctrl+C, the system generates the SIGINT signal, which interrupts and terminates the currently running program.

Common Signals in C++ with their Purpose

Here is a list of several types of signals in C++. Some of them are as follows:

Signal Description
SIGABRT It occurs when a program calls the abort() function. It leads to abnormal termination.
SIGFPE Triggered by errors in arithmetic, such as division by zero or numeric overflow.
SIGILL It is used when the processor detects an illegal or undefined machine instruction.
SIGINT It is used when the user interrupts the program (e.g., pressing Ctrl+C).
SIGSEGV It occurs when a program tries to access memory it doesn't have permission to, like dereferencing a null or invalid pointer.
SIGTERM It is commonly used to send the termination request using the kill or other methods.
SIGHUP It is used when a terminal is closed or the controlling process ends. It is used to detect hang-ups.
SIGQUIT It is similar to SIGINT, but also generates a core dump. It typically triggered by Ctrl+\.
SIGTRAP It is used by debuggers to trigger breakpoints or trap conditions.
SIGBUS It is raised when a process accesses memory incorrectly at a hardware level (e.g., misaligned memory access).
SIGUSR1 It is reserved for user-defined behavior. Developers can assign custom actions when this signal is received.
SIGUSR2 It is another user-defined signal, similar to SIGUSR1.
SIGALRM It is used when a timer set by the alarm() function expires. Commonly used for timeouts.
SIGCONT It is used to resume a process that has been paused (e.g., after SIGSTOP).
SIGSTOP It stops (pauses) a process immediately. It cannot be caught, blocked, or ignored.

Signal function

In C++, the signal function is a part of the signal-handling library, which is described in the header. It allows our program to respond to unexpected events, such as interrupts or unlawful operations by customizing signal handling.

Syntax

It has the following syntax:

Example

void (*signal(int sig, void *func(int)))(int);

In this syntax:

  • sig: The signal number (e.g., SIGINT, SIGTERM) that we want to handle.
  • func: A pointer to a function to be called when the signal is received.
  • return value: The return value is a pointer to the preceding signal handler.
  • Ways to Handle Signals with signal function

If we call the signal function, we have to specify how our program should respond to the provided signal. We have three primary options:

1) Default Action - SIG_DFL

In C++, it instructs the system to apply the signal's default behavior. For example, the program might terminate or create a core dump.

2) Ignore Signal - SIG_IGN

It allows the application to completely disregard the signal. The signal will be detected but not acted on. Execution continues because if nothing has happened.

3) Custom Function Handler

In C++, we can write our own signal-handling function. When the signal is received, the handler function is called, and we can specify how the application should respond (such as saving data, print a message, clean up resources, etc.).

C++ Signal Handling Example

Let us take an example to illustrate the signal handling in C++.

Example

Example

#include <iostream>

#include <csignal>

#include <unistd.h> 

using namespace std;    //using standard namespace

void my_handler(int signum) 

{

    cout << "\nInterrupt signal (" << signum << ") received. Program will now terminate.\n";

    exit(signum);

}

int main()   //main function

{

    signal(SIGINT, my_handler);

    cout << "The program is running. Press Ctrl+C to stop the flow..\n";

    while (true) 

    {

        cout << "Running...\n";

        sleep(1); 

    }

    return 0;

}

Output:

Output

The program is running. Press Ctrl+C to stop the flow..

Running...

Running...

Running...

Running...

Running...

Explanation:

In this example, we use an infinite loop to print "Running..." every second. After that, when the user presses Ctrl+C, the custom handler my_handler receives the SIGINT signal, prints a termination message, and gracefully stops the program.

raise Function

In C++, the raise function sends a signal to the currently running process. It is simple to use and suitable for testing signal handlers within the program.

Syntax

It has the following syntax:

Example

int raise(int signal_type);

In this syntax,

signal_type: The signal number to be sent for handling. It can take one of the following values:

  • SIGINT
  • SIGABRT
  • SIGFPE
  • SIGILL
  • SIGSEGV
  • SIGTERM
  • SIGHUP

Return Value: If the signal was properly delivered, it returns 0. If it fails, it returns a value other than zero.

C++ raise function Example

Let us take an example to illustrate the raise function in C++.

Example

Example

#include <csignal>

#include <iostream>

using namespace std;   //using standard namespace

void Signal_Handler(int s) 

{

    cout << "Interrupt is handled. Signal number is: " << s << endl;

    exit(s);  

}

int main()    //main function

{

    signal(SIGINT, Signal_Handler);

    raise(SIGINT);

    return 0;

}

Output:

Output

Interrupt is handled. Signal number is: 2

Explanation:

In this example, we use the raise function to manually generate the SIGINT signal, which is generally emitted by pressing Ctrl+C. When the raise(SIGINT) function is used, the custom handler Signal_Handler receives the signal and prints the signal number before terminating the application.

C++ Example using several raise Functions

Let us take an example to illustrate the several raise function in C++.

Example

Example

#include <iostream>

#include <csignal>

#include <unistd.h> 

using namespace std;   //using standard namespace

void Signal_Handler(int sig_no)

{

cout << "Interrupt is handled and the Signal number is: " << sig_no << endl;

}

void DisplayingMenu() 

{

    cout << "\nChoose a signal to raise which displayed below:\n";

    cout << "1. SIGINT (Ctrl+C simulation)\n";

    cout << "2. SIGILL (Illegal Instruction)\n";

    cout << "3. SIGALRM (Timer Alarm)\n";

    cout << "4. SIGFPE (Division by Zero)\n";

    cout << "5. SIGSEGV (Invalid Memory Access)\n";

    cout << "6. Exit out of the program.\n";

    cout << "Enter your choice which is from 1 to 6: ";

}

int main()   //main function

{

    signal(SIGINT, Signal_Handler);   

    signal(SIGILL, Signal_Handler);   

    signal(SIGALRM, Signal_Handler);  

    signal(SIGFPE, Signal_Handler);   

    signal(SIGSEGV, Signal_Handler);  

    int user_choice;

    while (true) 

    {

        DisplayingMenu();

        cin >> user_choice;

        switch (user_choice) 

        {

            case 1:

                raise(SIGINT); 

                break;

            case 2:

                raise(SIGILL); 

                break;

            case 3:

                alarm(1);      

                sleep(2);      

                break;

            case 4: 

            {

                int p = 30, q = 0;

                int ans = p / q; 

                cout << "The Result is: " << ans << endl;

                break;

            }

            case 5: 

            {

                int* pntr = nullptr;

                *pntr = 25;     

                break;

            }

            case 6:

                cout << "Exiting the program." << endl;

                return 0;

            default:

                cout << "Invalid choice. Try again.\n";

        }

    }

    return 0;

}

Output:

Output

Choose a signal to raise which displayed below:

1. SIGINT (Ctrl+C simulation)

2. SIGILL (Illegal Instruction)

3. SIGALRM (Timer Alarm)

4. SIGFPE (Division by Zero)

5. SIGSEGV (Invalid Memory Access)

6. Exit out of the program.

Enter your choice which is from 1 to 6: 1

Interrupt is handled and the Signal number is: 2

Choose a signal to raise which displayed below:

1. SIGINT (Ctrl+C simulation)

2. SIGILL (Illegal Instruction)

3. SIGALRM (Timer Alarm)

4. SIGFPE (Division by Zero)

5. SIGSEGV (Invalid Memory Access)

6. Exit out of the program.

Enter your choice which is from 1 to 6: 2

Interrupt is handled and the Signal number is: 4

Choose a signal to raise which displayed below:

1. SIGINT (Ctrl+C simulation)

2. SIGILL (Illegal Instruction)

3. SIGALRM (Timer Alarm)

4. SIGFPE (Division by Zero)

5. SIGSEGV (Invalid Memory Access)

6. Exit out of the program.

Enter your choice which is from 1 to 6: 3

Interrupt is handled and the Signal number is: 14

Choose a signal to raise which displayed below:

1. SIGINT (Ctrl+C simulation)

2. SIGILL (Illegal Instruction)

3. SIGALRM (Timer Alarm)

4. SIGFPE (Division by Zero)

5. SIGSEGV (Invalid Memory Access)

6. Exit out of the program.

Enter your choice which is from 1 to 6: 6

Exiting the program.

Explanation:

In this example, we register custom handlers for five distinct signals, and a menu-driven loop allows the user to manually raise signals, such as SIGINT, SIGILL, SIGALRM, SIGFPE, and SIGSEGV. Depending on the user's preference, each signal is purposefully triggered, and the handler prints the signal number rather than terminating the program unless a fatal error occurs.

kill function

Unlike the raise function, which only affects the current process, the kill function can send signals to any process (including the current one) as long as we have permission.

Syntax

It has the following syntax:

Example

int kill(pid_t pid, int signal_type);
  • pid: The process ID of the target process.
  • Returns 0 for success and -1 for failure.
  • C++ kill function Example

Let us take an example to illustrate the kill function in C++.

Example

Example

#include <iostream>

#include <csignal>

#include <unistd.h>

using namespace std;  //using standard namespace

void Handle_Signal(int s)

{

    cout << "Signal received: " << s << endl;

}

int main()  //main function

{

    signal(SIGINT, Handle_Signal);

    pid_t pid = getpid();

    kill(pid, SIGINT);

    return 0;

}

Output:

Output

Signal received: 2

Explanation:

In this example, we utilize the kill function to deliver the SIGINT signal to its own process, which is identified by the process ID returned by getpid. When the signal is sent, the custom handler Handle_Signal is invoked, which displays the signal number on the console.

Conclusion

In conclusion, the signal handling in C++ enables programs to respond to unexpected runtime events, such as interrupts or termination requests. If we use the signal function and define signal handler functions, we can intercept and manage signals, such as SIGINT or SIGTERM. It helps to improve the robustness of the program, which ensures safer shutdowns and controlled behavior during exceptional situations. Overall, signal handling is especially valuable in systems programming and resource-critical applications.

C++ Signal Handling FAQs

1) What signal is used when a user presses Ctrl + C?

The SIGINT signal is sent when the user pushes Ctrl + C in the terminal.

2) Can a program ignore a signal?

Yes, a program can ignore a signal by setting its handler to SIG_IGN with the signal function.

3) What signal cannot be caught, blocked, or ignored in C++?

No, program can catch, block, or ignore the SIGKILL and SIGSTOP signals.

4) What is the purpose of C++'s raise function?

In C++, the raise function is used to generate a signal within the running process manually.

5) What function sends a signal to another process using its process ID?

In C++, the kill function is used to deliver a signal to a specific process by referencing its PID.

Input Required

This code uses input(). Please provide values below: