GNU/Linux Signals

lh1008
6 min readApr 7, 2021
<a Emoji image from href=”https://emojipedia.org/waving-hand/” </a>

đź‘‹

  • What is a signal

A signal is a software interrupt delivered to a process. Signals are asynchronous notifications sent to a process within the same process to report an exceptional situation to an executing program.

Machines are constantly executing processes. Processes are executed by default or manually. This depends on whether we need to execute a program or turn on a machine. When the machine is turned on the first process called init is executed. After the first process is executed, others are forked and initiated. While they are executed, users or the machine itself can communicate to other processes using signals. When a signal is sent, the Operating System (OS) interrupts the target process to deliver the signal.

Signals can also be ignored, this is something we’re also going to discuss about in this blog.

—

  • Why do they exist

Signals are important in the communication process because they provide a way to communicate within processes. Signals interrupt any process execution if any particular event happens. Let’s check why processes are useful:

  • If a process is stopped, you can send a signal to restart or continue the process (this signal is called SIGCONT).
  • If a process tries to execute an unknown, illegal, malformed, or privileged instruction a SIGILLsignal is executed.
  • When manually a user wants to interrupt a process, Ctrl + C will send a SIGINTsignal.
  • When a process needs to be terminated immediately, SIGKILLdoes the job.
  • When the user requests the process to quit and perform a core dump, ctr + \sends a SIGQUIT signal.
  • SIGSYS signal is sent when a bad argument is passed to a system call.
  • SIGTERMis a signal sent to request a process termination. The main difference with SIGKILL is that the signal can be caught, interpreted, or ignored, helping the process release resources and save any data. SIGILL can also be caught, interpreted, or ignored.

You can see that processes can either be manually executed or be an internal machine process communication to interrupt the execution of a process and change the state of the process itself.

Signals fall into three major categories: errors, external events, and explicit requests.

—

  • When are they delivered and by whom

When the process is generated, it becomes pending. The signal will be pending for a short period of time and then is delivered to the process either by the kernel or another process. These are some events that can cause a signal:

  • A program error such as dividing by zero or issuing an address outside the valid range.
  • A user requests to interrupt or terminate the program.
  • The termination of a child process.
  • Expiration of a timer or alarm.
  • A call to kill or raise by the same process.
  • A call to kill from another process.
  • An attempt to perform an I/O operation that cannot be done.

—

  • What are the default actions of signals

A default signal handler is associated with every signal that the kernel runs when handling a signal. The action that a script performs when it receives a signal is called a default action.

Each signal has a current disposition that determines how processes behave when the signal is delivered. The entry “action” of signals, specify a default disposition for each signal. These dispositions are known as follows:

  • Term Default action is to terminate the process.
  • Ign Default action is to ignore the signal.
  • Core Default action is to terminate the process and dump core.
  • Stop Default action is to stop the process.
  • Cont Default action is to continue the process if it is
    currently stopped.

—

  • What happens to a process when it receives a signal without handling it

When a signal is delivered to a process without any handling, the process must be handled immediately, interrupting the executing process.

—

  • What happens to a process when it receives a signal and handles it

Two basic events can happen:

  1. You can have the handler function note that a signal arrived by adding some global data structures to later return normally.
  2. You can have the handler function terminate the process or transfer control to a point where it can recover from the same place where the signal was received.

Two functions you can use to handle processes within the system are signal( ) and sigaction( ). Let me show you how signal( ) works.

signal( )- sets the disposition of the signal signum and handler. Signum is the signal itself, e.g. SIGINT, SIGQUIT , and the handler could either be a default action or a data structure on how to handle the signal. Maybe you want to print a message once the signal arrives, or you want to execute something else before it returns to normal action. Let me share you a code on how this would look like when using a signal( ) function.

We’re going to create and compile a .c file so you can check this yourself. In this example, we’re going to use emacs text editor, but you can use any text editor of your preference.

Open your terminal. Locate yourself in a directory where you would like to save the files and execute them. Type the following command in your terminal to open and create the file handle_signal.c:

$ emacs handle_signal.c

This will create and automatically open the handle_signal.c file. Copy/paste the below code in the file:

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
/**
* handler - handler entry
* Desc: function to print signal
* @signum: int signal
* Return: print 'Gotcha!'
*/
void handler(int signum)
{
printf("Gotcha! [%d]\n", signum);
fflush(stdout);
}
/**
* handle_signal - entry to handle_signal
* Desc: handler_signal that set a handler for a signal SIGINT
* Return: 0 in success or -1 on error
*/
int handle_signal(void)
{
if (signal(SIGINT, handler) != SIG_ERR)
return (0);
else
return (-1);
}
/**
* main - Entry point
*
* Return: EXIT_SUCCESS or EXIT_FAILURE
*/
int main(void)
{
int i;
if (handle_signal() == -1)
{
printf("Failure\n");
return (EXIT_FAILURE);
}
for (i = 0; ; i++)
{
printf("[%d] Wait for it ...\n", i);
sleep(1);
}
return (EXIT_SUCCESS);
}

Close and save the file. Type the following command to compile the .c file using gcc:

$ gcc handle_signal.c -o signal

This will produce an executable file called signal. Execute the file with the following command:

$ ./signal

This will initiate the program and print the following message in an “infinite” loop:

[0] Wait for it ...
[1] Wait for it ...
[2] Wait for it ...

While the message is being printed, press Ctrl + C to send the SIGINT signal to your executed program. You will see the message ^CGotcha! [2] printed in your terminal:

[0] Wait for it ...
[1] Wait for it ...
[2] Wait for it ...
^CGotcha! [2]
[3] Wait for it ...
[4] Wait for it ...
[5] Wait for it ...

The [2] is the integer that identifies the signal SIGINT and the Gotcha! is what the program prints so you can see the program is working properly.

To close the program send the signal SIGQUIT by pressing Ctrl + \. This will Quit and (core dumped) the program.

[0] Wait for it ...
[1] Wait for it ...
[2] Wait for it ...
^CGotcha! [2]
[3] Wait for it ...
[4] Wait for it ...
[5] Wait for it ...
^\Quit (core dumped)

—

  • What happens when no programs are executed and the terminal is opened without activity

Well, no signals will be sent. Processes will run as they usually do until any particular event happens.

This blog helped us describe and give a wider explanation on how signals are used within the system. We didn’t describe them all and neither went into a deep tech debt explanation on them but we can now explain to anyone what signals within the GNU/Linux systems are.

References:

--

--

lh1008

Life just keeps on happening in the eternal present. Keep building your present.