• LOGIN
  • No products in the cart.

Introduction to Signals and Interrupt Handlers

A Unix signal is a software interrupt which can be sent from one process to another, or from one thread to another.
When we terminate a process by typing ‘kill –9’, we are sending signal 9 to the process ID which follows this command. The shell passes the ‘kill’ command to the kernel which logs the pending interrupt in the interrupt table of the process. When it becomes the turn of the process to run, the kernel checks the signal against the signal mask lodged with the process and, if the signal is permitted to be handled, performs the action defined in the mask. Unfortunately, signal 9 is not of this type, so the process is terminated.

By Mark Sitkowski C.Eng., M.I.E.E Design Simulation Systems Ltd

Apart from terminating processes, the kernel uses signals to inform processes of interesting occurrences, like the expiry of a timer, or the termination of a child process, and to warn of forthcoming disasters like power failures and disk overflows.
Although most signals have predetermined functions, two are undefined, and are reserved for use by users. Not surprisingly, they are called

SIGUSR1 and SIGUSR2.

How Do We Send a Signal to a Process?
First, any code which uses interrupt, has to include the signal definition header.

#include 

This file is basically a cross-reference of the human-readable signal names, and their numbers, which are the only way the kernel recognizes them.
The actual system call, which sends the signal, is the same as its command line counterpart. To terminate a process, we use:

int kill(pid_t process_id, int signal_number_or_name);

Obviously, before this call can succeed, we must have permission to use it. For the examples which we shall consider, there is no problem since our parent process owns all child processes. However, if we wanted to send a signal to a process owned by another user, the question of user and group ID’s would need to be taken into account.
The two arguments to kill(), are the process ID of our intended victim, and the number or symbolic name of the signal we intend to send it.
Since we are mainly interested in the use of signals as a means of communication, rather than their use to slaughter innocent processes, we will be passing them between parent and child. The process ID’s of all children are known to a parent, from the return value of fork(), and the process ID of a parent is returned by the getppid() system call.
For example, if we needed to tell our parent process that we had encountered a serious problem, we would use the following call:

If(kill(getppid(), SIGUSR1) == -1){
 perror(“Tell parent failed”);
 }

Note that we use SIGUSR1, rather than ‘16’. Some of the numeric values assigned to signals (especially SIGUSR1 and SIGUSR2) vary from one version of Unix to another. Therefore, in the interests of portability, we will only refer to the signals by name.
Most operating systems have a version of the kill() system call which can be directed to a particular thread from within the process where the thread runs. Also, the individual threads which normally inherit their interrupt mask from the parent thread, which created them, can set up their own interrupt mask which can be different to that of the parent process. When a signal is sent to a process which has multiple threads running, it is delivered to the thread best able to accept it. So, there is a very strong possibility that the wrong combination of interrupt masks can lead to some interesting bugs, like thread deadlock.
Unless it is absolutely necessary, and all of the possibilities can be accounted for, it is a good idea to leave each thread with its default interrupt mask.
However, given that that has been taken care of, individual threads can be terminated or signaled with the pthread_kill() function, which mirrors the kill() function’s syntax, with the substitution of the process ID by the thread ID. Since the thread ID was obtained at the time the thread was created, it follows that only threads which are part of the calling process can be signaled by it.:

if(pthread_kill(thread_ID, SIGUSR1) == -1){
 perror(“Tell parent failed”);
 }

The Most Useful Signals

The signal man page, in section 3head of the Unix manual, contains a complete list of all, thirty-odd, signals, with their definitions and numeric values. We will only discuss those signals that are useful to us, in the context of multi-dimensional programming.

SIGCHLD
This is number 18 on Sun Solaris, and is sent by the kernel to the parent process each time a child process terminates. If we design our code carefully, we can keep track of our children by counting the number of SIGCHLD signals received.

SIGALRM
The Unix kernel has an alarm clock, which can be used by a process, to wake itself up, put itself to sleep, or interrupt its normal processing, to perform some important task. SIGALRM is generated by the system alarm clock, which is set by calling alarm(time_out) and turned off by calling alarm(0).

SIGUSR1 and SIGUSR2
These can be anything we want them to be. Since there are enough predefined termination signals, we would probably use them to communicate events between processes, such as the state of a semaphore, the readiness of a process to send data or the presence of a file. It should be noted that, unlike the other signals, the signal numbers differ from one version of Unix to another. Solaris uses 15 and 16, while some versions of Linux use 30 and 31, or 25 and 26.

SIGSTOP
There may be occasions when we want a process to stop execution, but be ready to run again. When SIGSTOP is received, the process, and each of its threads, stops executing. We can restart it again, by sending it a SIGCONT signal. Alternatively, the process can be restarted by sending it a different signal which is set to be caught in the interrupt handler. From the interrupt handler, we can either execute return(), call another function, or perform longjmp() to a fixed place in the code.
It should be mentioned, however, that if SIGSTOP or SIGCONT is sent to a child process, the kernel will send its parent a SIGCHLD signal, unless this is set to be ignored. Therefore, care should be exercised when using these signals.

SIGHUP
This is the signal passed by ‘kill –1’ to a process. It may be considered as a hint that the process should die. It is intentionally left, able to be caught, so that a process can perform the last rites before quitting. Frequently, this signal is used as a reconfiguration hint, forcing the process to re-read startup files, environment variables and other sources of configuration data.

SIGINT
When control-C is entered on the keyboard, it sends a ‘kill –2’ signal to the process attached to the window of which, the keyboard is the standard input. The signal was designed to do, from the keyboard, what SIGHUP does from the command line.

SIGQUIT
When it is desired to obtain the core image of a process, for examination or to measure how much memory the process is using, ‘kill –3’ will force the process to dump core. A ‘core dump’ (the term is a relic of the days when memory was a cubic foot of magnetic cores) is a dump of the data segment, text segment and stack of a running process. Various fault conditions within the process will cause the kernel to send it this signal. But there are occasions when we need to make a healthy process dump core. This signal cannot be caught.

SIGKILL
In the early Unix manuals, SIGKILL or ‘kill –9’, was described as ‘kill with extreme prejudice’. It instantly terminates the process and cannot be caught.

SIGTERM
This signal has the same effect as the SIGKILL.

September 15, 2017

Leave a Reply

Be the First to Comment!

Notify of
avatar
wpDiscuz
© HAKIN9 MEDIA SP. Z O.O. SP. K. 2013