Introduction to UNIX Signals and System Calls

First published: Sept 2007
Last update: Dec 2007

Whether you are creating applications for Mac OS X, Linux, BSD or Solaris, I would recommend that you develop a good mental model of UNIX signals and system calls.

This can be challenging as most material on this topic is way too in-depth and specific to capture casual readers whose objective is not to become system programmers or UNIX gurus, but rather to get more familiar with the platform that they are using.

These casual readers are the main audience for this article: keep reading if you want to get a good overall understanding of signals and system calls without diving too deeply into UNIX internals and operating system theory. In this way, the next time that you cannot make any sense of the latest trick that the system is playing on you, you will be able to leverage system tools such as strace, DTrace or SystemTap to figure it all out.

1. Why Even Bother Learning This?

“A good conceptual model allows us to predict the effect of our actions. Without a good model we operate by rote, blindly; we do operations as we were told to do them; we can’t fully appreciate why, what effects to expect, or what to do if things go wrong. As long as things works properly we can manage. When things go wrong, however, or when we come upon novel situation, then we need a deeper understanding, a good model.” Donald A. Norman, The Design of Everyday Things

When using a computer it is always good to have some kind of understanding of the software stack that you are using. Otherwise when the system does not behave as expected you are lacking the appropriate mental model and metaphor that will help you come up with a way to resolve the problem.

Getting a good overall understanding of your software stack is even more critical if you are creating or deploying software applications. In these cases it is pretty much guaranteed that, at some point, something will go wrong.

The better you grasp the fundamental principles of your environment, the quicker you will recover when things go awry. In particular it is important to get a good understanding of your operating system’s fundamental mechanisms, even if you concentrate on a higher level of the stack – say writing web applications in a high-level language such as Java, Ruby or Python.

My “Troubleshooting Ruby Processes” book, for instance, demonstrates how to leverage system tools to resolve complex problems when deploying Ruby and Ruby on Rails applications &ldot; provided that you acquire a basic understanding of UNIX signals and system calls!

2. The Big Picture

2.1. The Partition Between Kernel and User Space

In the UNIX operating system, applications do not have direct access to the computer hardware (say a hard-drive). Applications have to request hardware access from a third-party that mediates all access to computer resources, the Kernel.

Kernel Diagram

As you can see from the above diagram, the Kernel is a central component of most computer operating systems. Its main responsibilities are to:

The Kernel can also be seen as one abstraction layer part of the traditional computer architecture design in which each layer relies solely on the functions of the layers beneath itself. Its mission is to protect users and applications from each others and to provide a simple metaphor that shields applications from most of the system complexity.

Now that we understand better what constitutes the Kernel Space, you might wonder what User Space is. Well it is actually quite simple: User Space is everything else! More exactly, all the software running on a UNIX system except the Kernel (and its device drivers) is running in User Space.

2.2. Switching Between User Space and Kernel Space

If processes cannot directly access hardware resources, there must be a way to switch from the User Space to the Kernel Space, right? There are actually more than one way:

You might be wondering whether a rogue user process could be not so polite and try to circumvent the Kernel to access system resources directly. Well, luckily that would not work: The Kernel mode is not only a software but also a hardware state. Modern processors offer a privileged execution mode, often referred to as Supervisor Mode in which the Kernel, and only the Kernel runs. If it is not in Supervisor Mode, the processor will deny privileged operations such as modifying special registers, disabling interrupts, accessing memory management hardware or computer peripherals.

The idea of having two different modes to operate on, User Space and Kernel Space, is a building block for privilege separation. It guarantees that a User Space process will never crash the whole system, even a malicious or poorly written one (provided the Kernel is stable).

2.3. System Calls

The only way for an application in User Space to explicitly trigger a switch to Kernel Mode is to issue a system call. Therefore system calls constitute the interface between processes and the operating system. Each system call provides a basic operation such as opening a file, getting the current time, creating a new process, or reading a character. In this way system calls can be viewed as regular function calls, if it were for the fact that they transfer control to the UNIX Kernel. System calls essentially are synchronous calls to the operating system.

Kernel Diagram

When a program invokes a system call, it is interrupted3 and the system switches to Kernel space. The Kernel then saves the process execution context (so that it can resume the program later) and determines what is being requested. The Kernel carefully checks that the request is valid and that the process invoking the system call has enough privilege. For instance some system calls can only be called by a user with superuser privilege (often referred to as root).

If everything is good, the Kernel processes the request in Kernel Mode and can access the device drivers in charge of controlling the hardware (e.g. reading a character inputted from the keyboard). The Kernel can read and modify the data of the calling process as it has access to memory in User Space (e.g. it can copy the keyboard character into a buffer that the calling process has access to). However, it will not execute any code from a user program, for obvious security reasons.

When the Kernel is done processing the request, it restores the process execution context that was saved when the system call was invoked, and control returns to the calling program which continues executing.

From the perspective of the calling process a system call is synchronous. In practice though, the Kernel will not necessarily directly resume the program that invoked the system call when it is done processing the request. Remember that the Kernel is implementing multitasking and ensures a somewhat fair access to the processor? So when the Kernel returns control to the User Space it gets to pick which user program to resume based on his process scheduler heuristics. Actually, in many cases, not returning control immediately to the invoking program is a good thing, as it may take a while for the Kernel to get data from the hardware. This is especially true for I/O4 operations such as reading data from the network.

It is worthwhile to get familiar with the most common UNIX System Calls as they constitute the interface to the core of the operating system and permeates the programming metaphor of the overall platform (like the fork/exec pattern for instance).

2.4. Signals

Signals offer another way to transition between Kernel and User Space. While system call are synchronous calls originating from User Space, signals are asynchronous messages coming from Kernel space. Signals are always delivered by the Kernel but they can be initiated by:

Kernel Diagram

So a signal is an asynchronous message, but what happens exactly when a process receives it? Well… it depends. For each signal, a process can instruct the Kernel to either:

The overall dynamic for signal delivery is quite simple:

  1. When a process receives a signal that is not ignored, the program immediately interrupts its current execution flow5.
  2. Control is then transferred to a dedicated signal handler, a custom one defined by the process or the system default.
  3. Once the signal handler completes, the program resumes where it was originally interrupted.

In practice, though, the mechanics used by the Kernel to send a signal are more involved and consist of two distinct steps: generating and delivering the signal.

The Kernel generates a signal for a process simply by setting a flag that indicates the type of the signal that was received. More precisely, each process has a dedicated bitfield used to store pending signals; For the system, generating a signal is just a matter of updating the bit corresponding to the signal type in this bitfield structure. At this stage, the signal is said to be pending.

Before transferring control back to a process in user mode, the Kernel always checks the pending signals for this process. This check must happen in Kernel space because some signals can never be ignored by a process – namely SIGSTOP and SIGKILL (you trigger SIGKILL with the infamous kill -9 command).

When a pending signal is detected by the Kernel, the system will deliver the signal by performing one of the following actions:

A crucial point here is to realize that the Kernel triggers the signal handler, when the signal is delivered, not when the signal is generated. As signal delivery only happens when the system schedules the target process as active in a multitasking system (just before switching back to User Mode) there can be a significant delay between signal generation and delivery.

Finally a process has one last option when it comes to signals. It can instruct the Kernel to block the delivery of a specific signal. If a signal is blocked, the system still generates it and the signal is considered pending. Nevertheless the Kernel will not deliver a blocked signal until the process unblocks it. Signal blocking is typically used in critical sections of code that must not be interrupted.

3. Overview of UNIX Signals Semantics and API

3.1. Common Signals

Most UNIX systems define about 30 signals. The most commons are described in the table below.

NameNumberDefault ActionSemantics
SIGHUP1TerminateHangup detected on controlling terminal or death of controlling process
SIGINT2TerminateInterrupt from keyboard. Usually terminate the process. Can be triggered by Ctrl-C
SIGQUIT3Core dumpQuit from keyboard. Usually causes the process to terminate and dump core. Cab be triggered by Ctrl-\
SIGILL4Core dumpThe process has executed an illegal hardware instruction.
SIGTRAP5Core dumpTrace/breakpoint trap. Hardware fault.
SIGABRT6Core dumpAbort signal from abort(3)
SIGFPE8Core dumpFloating point exception such as dividing by zero or a floating point overflow.
SIGKILL9TerminateSure way to terminate (kill) a process. Cannot be caught or ignored.
SIGSEGV11Core dumpThe process attempted to access an invalid memory reference.
SIGPIPE13TerminateBroken pipe: Sent to a process writing to a pipe or a socket with no reader (most likely the reader has terminated).
SIGALRM14TerminateTimer signal from alarm(2)
SIGTERM15TerminateTermination signal. The kill command send this signal by default, when no explicit signal type is provided.
SIGUSR130,10,16TerminateFirst user-defined signal, designed to be used by application programs which can freely define its semantics.
SIGUSR231,12,17TerminateSecond user-defined signal, designed to be used by application programs which can freely define its semantics.
SIGCHLD20,17,18IgnoreChild stopped or terminated
SIGCONT19,18,25Continue / IgnoreContinue if stopped
SIGSTOP17,19,23StopSure way to stop a process: cannot be caught or ignored. Used for non interactive job-control while SIGSTP is the interactive stop signal.
SIGTSTP18,20,24StopInteractive signal used to suspend process execution. Usually generated by typing Ctrl-Z in a terminal.
SIGTTIN21,21,26StopA background process attempt to read from its controlling terminal (tty input).
SIGTTOU22,22,27StopA background process attempt to write to its controlling terminal (tty output).
SIGIO23,29,22TerminateAsynchronous I/O now event.
SIGBUS10,7,10Core dumpBus error (bad memory access)
SIGPOLL TerminateSignals an event on a pollable device.
SIGPROF27,27,29TerminateExpiration of a profiling timer set with setitimer.
SIGSYS12,-,12Core dumpInvalid system call. The Kernel interpreted a processor instruction as a system call, but its argument is invalid.
SIGURG16,23,21IgnoreUrgent condition on socket (e.g. out-of-band data).
SIGVTALRM26,26,28TerminateExpiration of a virtual interval timer set with setitimer.
SIGXCPU24,24,30Core dumpCPU soft time limit exceeded (Resource limits).
SIGXFSZ25,25,31Core dumpFile soft size limit exceeded (Resource limits).
SIGWINCH28,28,20IgnoreInforms a process of a change in associated terminal window size.

4. References

  1. Processor executing computer programs.

  2. Unless the two processes previously explicitly agreed to share this memory zone.

  3. Usually by means of a software trap.

  4. Input/Output

  5. More precisely execution can be interrupted during any non-atomic instruction.

Original web site design by: JFX diz*web.