Signals, or software interrupts, are external, asynchronous events used to alter the course of a program. These may occur at any time during the execution of a program. Because of this, they differ from other methods of inter-process communication, where two processes must be explicitly directed to wait for external messages; for example, by calling read on a pipe (see chapter 5).
The amount of information transmitted via a signal is minimal, just the type of signal, and although they were not originally intended for communication between processes, they do make it possible to transmit atomic information about the state of an external entity (e.g. the state of the system or another process).
When a process receives a signal, there are four possible outcomes:
There are several types of signals, each associated with a particular event. Table 4 lists some of them with their default behaviors.
|Hang-up (end of connection)||Termination|
|Strong interruption (||Term. & core dump|
|Arithmetic error (division by zero)||Term. & core dump|
|Very strong interruption (cannot be ignored)||Termination|
|Memory protection violation||Term. & core dump|
|Writing to a pipe without readers||Termination|
|Temporary halt (||Suspension|
|Resuming a stopped process||Ignored|
|A child process died or was stopped||Ignored |
The signals received by a process come from several possible sources:
ctrl-C, the console operator sends the
sigintsignal to the processes controlled by her terminal (that were not already put in the background). In the same way, the
sigquitsignal is sent by typing
ctrl-\1. When the terminal is closed (either through voluntary disconnection or owing to a disconnected network link), the
sighupsignal is sent.
kill. This makes it possible to send a specific signal to a specific process. For example,
kill -KILL 194sends the
sigkillsignal to the process with id 194, which causes the process to be killed.
kill(the preceding example being a specific case).
The system call kill makes it possible to send a signal to a process.
The first parameter is the process id of the destination process and
the second the signal number to send. An error occurs if we attempt to
send a signal to a process not owned by the user. A process may send
signals to itself. When the
kill system call returns, it is
guaranteed that the signal was delivered to the destination
process. If a process receives the same signal in rapid succession it
will execute the code associated with the signal only once. Therefore
a program cannot count the number of times it receives a signal,
rather only the number of times it responds to it.
The system call alarm makes it possible to schedule interruptions based on the system clock.
alarm s returns immediately but causes the
signal to be sent to the calling process at least
s seconds later
(note that there is no guarantee on the maximum wait time). The call returns
the number of seconds remaining to an alarm scheduled by a previous call.
0, the effect is simply to cancel an earlier alarm.
The system call signal makes it possible to modify the behavior of a process when it receives a signal of a certain type.
The first argument is the signal number and the second argument, a value of type signal_behavior, indicates the desired behavior for the signal. With:
|The signal is ignored.|
|The default behavior occurs.|
|The function |
Forking a process with the system call fork
preserves signal behavior: the initial definitions for the child are
those of the parent at the time when
fork was executed. The
execve system call sets all the behaviors to
Signal_default except that signals ignored before are still
Occasionally we want to log-off or end a session while allowing
background tasks (large calculations, “spyware” programs, etc.)
to continue to run. If this is desired, processes which normally
exit on receiving
sighup (sent at the time the user disconnects)
should be prevented from doing so. The Unix command
executes the command
cmd arg1 ... argn in a way unaffected by
sighup (certain shells execute
automatically for all processes launched as background tasks). Here’s how
to implement this in three lines:
The system call execvp preserves the fact that
sighup is ignored.
Carefully exiting when a program is misbehaving. For example,
a program like
tar can try to save important information
in a file or destroy the corrupted file before terminating. For this
it is possible to include the following lines at the beginning of the program:
where the function
quit is of the form:
Capturing user-initiated interruptions. Some interactive programs
need to return to a main control loop when a user
ctrl-C. For this we just need to raise an exception when the
sigint signal is received.
To carry out periodic tasks (animations, etc.) interleaved with the execution of the main program. For example, here is how to create “beep” sounds every 30 seconds, regardless of the activity of the main program (calculations or input/output).
Signals are useful for asynchronous communication — indeed, it is their raison d’être — but this asynchronous nature also makes them one of the major difficulties of system programming.
The signal handling function is executed asynchronously and thus pseudo-concurrently with the main program of the process. As signal handling functions cannot return a value, they usually modify global variables. This can result in race conditions between the signal handler and the main program if they try to modify the same variable. As explained in the next section one solution is to temporarily block signals when this variable is accessed by the main program.
In fact, OCaml does not treat signals in a strictly asynchronous fashion. On receiving a signal, OCaml records the receipt of the signal but the signal handling function will only be executed at certain checkpoints. These are frequent enough to provide the illusion of asynchronous execution. The checkpoints typically occur during allocations, loop controls, or interactions with the system (particularly system calls). OCaml guarantees that a program that does not loop, does not allocate, and does not interact with the system will not have its execution interleaved with that of a signal handler. In particular, storing an unallocated value (integer, boolean, etc. — but not a float!) in a reference cell cannot result in the race condition described above.
Signals may be blocked. Blocked signals are not ignored, but put on standby, generally to be delivered later. The sigprocmask system call makes it possible to change the mask for incoming signals:
sigprocmask cmd sigs changes the list of blocked signals and
returns the list of signals that were blocked before the execution of
the function. This makes it possible to later reset the mask to its
previous state. The argument
sigs is a list of signals and
a value of type sigprocmask_command which determines the
effect of the call:
|The signals |
|The signals |
|The signals |
A typical usage of
sigprocmask is to mask certain
Often, one has to guard against possible errors by using the following pattern:
Certain system calls can be interrupted by unignored signals. These
system calls are known as slow calls, which can take an
arbitrary amount of time (for example terminal i/o,
select, system, etc.). If
an interruption occurs, the system call is not completed and raises
EINTR. However file i/o is not interruptible:
although these operations can suspend the running process to execute
another process for disk i/o, when this occurs the interruption will
always be brief if the disk functions correctly. In particular, the
throughput of data depends only on the system, and not another user’s
Ignored signals are never delivered and a masked signal is not
delivered until unmasked. But in all other cases we must protect our
system calls against unwanted interruptions. A typical example is a
parent waiting for the termination of a child. In this case, the
parent executes waitpid
 pid where
pid is the
process id of the child. This is a blocking system call, it is thus
slow and could be interrupted by the arrival of a signal,
especially since the
sigchld signal is sent to the parent when a
child process dies.
Misc define the function
makes it possible to repeat a system call when it is interrupted by a
signal, i.e. when the
EINTR exception is raised.
To wait on a child correctly, call
restart_on_EINTR (waitpid flags) pid.
Children can also be recovered asynchronously in the signal handler of
sigchld, especially when their return value does not matter to
the parent. But when the process receives the
sigchld signal, it
is not possible to know the exact number of terminated processes, since if
the signal is received several times within a short period of time the
handler is invoked only once. This leads to the library function
Misc.free_children function to handle the
non-blocking mode (option
WNOHANG) to recover any dead children
and repeats until either there are only live children (zero is
returned instead of a child id) or there are no children (
Note that it is not important to guard against the
waitpid is non-blocking when called with the
system in the
Unix module is simply defined as:
The specification of the
system function in the C standard
library states that the parent ignores
sigquit signals and masks the
sigchld signal during the
command’s execution. This makes it possible to stop or kill the child
process without affecting the main program’s execution.
We prefer to define the function
system as a specialization of the
more general function
exec_as_system which does not necessarily
go through the shell.
Note that the signal changes must be made before the call
fork is executed because the parent could receive signals
sigchld if the child were to finish immediately) before it
proceeds. These changes are reset for the child on line 11
before executing the command. Indeed, all ignored signals
are preserved by
exec and their behavior is
exec system call uses the
default behavior of signals except if the calling process ignores a
signal in which case it also does.
Finally, the parent must also reset the changes immediately after the
call, even if an error occurs. This is why
try_finalize is used on line 15.
Since the earliest versions of Unix, time has been counted in seconds.
For compatibility reasons, therefore, one can always measure time in seconds.
The current time is defined as the number of seconds since January 1st, 1970
00:00:00 gmt. It is returned by the function:
The sleep system call can pause the execution of a program for the number of seconds specified in its argument:
However, this function is not primitive. It is programmable with
more elementary system calls using the function
sigsuspend l system call temporarily suspends the signals in the
l and then halts the execution of the program until the reception
of a signal which is not ignored or suspended (on return, the
signal mask is reset to its old value).
Now we may program the
Initially, the behavior of the
sigalrm signal does nothing. Note
that “doing nothing” is the same as ignoring the signal. To
ensure that the process will be awakened by the reception
of the signal, the
sigalrm signal is put in an unblocked
state. Then the process is put on standby by suspending all other
signals which were not already suspended (
old_mask). After the alarm
is signaled, the preceding modifications are erased. (Note that
line 9 could be placed immediately after
line 2 because the call to
the signal mask.)
In more modern versions of Unix, time can also be measured in microseconds.
In OCaml, time measured in microseconds is represented by a float.
The gettimeofday function is the equivalent of the
function for modern systems.
In present-day Unix each process is equipped with three timers, each measuring time from a different perspective. The timers are identified by a value of type interval_timer :
|Real time (|
|User time (|
|User time and system time (|
The state of a timer is described by the interval_timer_status
type which is a record with two fields (each a
it_intervalis the period of the timer.
it_valueis the current value of the timer; when it turns
sigvtalrmis sent and the timer is reset to the value in
A timer is therefore inactive when its two fields are
The timers can be queried or modified with the following functions:
The value returned by
setitimer is the old value of
the timer at the time of the modification.
new_timer k f should create a new timer of the
k starting the action
f, and inactive on creation;
set_timer t should set the value of the timer
(and return the old value).
Modern versions of Unix also provide functions to handle dates, see the structure tm which allows dates and times to be expressed according to a calendar (year, month, etc.) and the conversion functions: gmtime, localtime, mktime, etc.
Owing to their asynchronous nature, the use of signals for inter-process communication presents some limitations and difficulties:
Signals offer only a limited form of asynchronous communication but carry all the difficulties and problems associated with it. If possible, it is therefore better not to use them. For example, to wait for a small amount of time, select can be used instead of alarms. But in certain situations signals must be taken into account (for example in command line interpreters).
Signals are possibly the least useful concept in the Unix system. On
certain older versions of Unix (System V, in particular) the behavior
of a signal is automatically reset to
Signal_default when it is
received. The signal handling function can of course register itself
again. For example in the “beep” example on
page ??, it would be necessary to write:
However the problem is that the signals that are received between the
instant were the behavior is automatically reset to
Signal_default and the moment were
set_signal is invoked are
not treated correctly and depending on the type of the signal they may
be ignored or cause the process to die instead of invoking the signal
Other flavors of Unix (bsd or Linux) provide better support: the behavior associated with a signal is not altered when it is received, and during the handling of a signal other signals of the same type are put on hold.