Using Signals
General
In unix/linux one can send signals to a program or a script. Some common signals are:
HUP | Terminal log-out | |
INT | Interrupt signal (Ctrl-C is pressed) | |
TERM | Termination of the process (as if 'kill' was called) | |
KILL | Kill of the process (as if 'kill -9' was called) | |
STOP | Stop the process | |
USR1 | Unspecified user signal (can be given with 'kill -USR1') | |
USR2 | Unspecified user signal (can be given with 'kill -USR2') |
Signals can be given using the 'kill' command, but can also be given by the system. One important instance of this is that on SciNet, a TERM signal is sent to a jobs whose requested time is over, after which the job has about 30 seconds to clean up.
As explained below, a program or script can be set up to listen for a particular kind of signal (except for KILL and STOP, which cannot be blocked). When it receive a signal of that kind, its execution is interrupted and a call is made to a function, specified earlier in the program. It is up to the program or script what this signal handling function does but it is a good idea to make the action appropriate for the event that triggers the signal. For instance, a TERM signal should be handled as a request to terminate the application. The user signals USR1 and USR2 do not have a pre-designated meaning and can be used for application-specific actions such as checkpointing.
Trapping signals in bash scripts
To trap signals in a bash script, one has to bind a specific signal to a command or function with the trap command. For example, <source lang="bash">
- !/bin/bash
trap "echo Term was trapped.; exit" TERM for ((i=0;i<60;i++)) do
echo $i sleep 1
done </source> Running this script in the background and sending it a TERM command ('kill -TERM [pid]') will cause the message 'Term was trapped.' to be printed. Notes:
- The trapped command has to use 'exit' explicitly to stop the script's execution.
- The 'sleep' command in bash cannot be interrupted, which is why the script contains a succession of 1 second 'sleep's.
Another useful example is given on the wiki page about using ramdisk.
Trapping signals in C
To trap signals in a c program, one has to include the signal.h header file: <source lang="c">
- include <signal.h>
</source> and provide a signal handler function <source lang="c"> void term_trap(int sig) {
/* do something */
} </source> which is linked to the specific signal somewhere as follows: <source lang="c"> signal(SIGTERM, term_trap); </source> Note that the names of signals are prepended with "SIG" in signal.h.
A minimal example: <source lang="c">
- include <stdio.h>
- include <signal.h>
void term_trap(int sig) {
printf("Term was trapped.\n");
}
int main() {
signal(SIGTERM, term_trap); sleep(60);
} </source> Note that the names of the signals are prepended with SIG in C. To test this program:
- save the above code as sigex.c
- compile: icc -O3 -xHost sigex.c -o sigex
- run: sigex&
- note the pid (process idenitfier)
- you can then give the process the term signal with kill -TERM [pid].
Trapping signals in C++
The same method as for a C program works in C++, except the header file is 'csignal'. For example: <source lang="c">
- include <iostream>
- include <csignal>
void term_trap(int sig) {
std::cout << "Term was trapped.\n";
}
int main() {
signal(SIGTERM, term_trap); sleep(60);
} </source>
Trapping signals in Fortran
The following works at least with gfortran. <source lang="f77"> PROGRAM SIGEX
INTRINSIC SIGNAL EXTERNAL TRAP_TERM CALL SIGNAL (15, TRAP_TERM) CALL SLEEP (60)
END SUBROUTINE TRAP_TERM
PRINT *, "Term was trapped."
END </source>
More to come.