Signals

Signals are software interrupts that are used to notify a running process that some extraordinary condition has occurred.

For example, if a process attempts to divide by zero, the CPU traps to the kernel with an interrupt and the kernel sends a signal (whose name is SIGFPE) to the process.

A process can handle a signal in 4 ways.  This is called the disposition of the signal.  The disposition can be to

  1. Ignore the signal and continue running.
    1. Some signals cannot be ignored or caught.
  2. Block the signal. Put signal in a waiting queue.
    1. Used to protect critical regions of the user code.
  3. Let a default action defined by the kernel to occur.
    1. See default actions below. Most are terminate.
  4. Provide a handler function that is called by the kernel when the signal occurs.
    1. This is called catching the signal.

Signal Generation

Signals can be generated by:

  1. The Kernel: Hardware exceptions cause interrupts to the kernel. The kernel interrupt handler may generate a signal to the process that caused the exception.
  2. Users: Users can generate signals using terminal keys to interrupt the running process. The default action is to terminate the process.
    • The interrupt key (often CTRL+C or the DELETE key) sends SIGINT
    • The quit key (often CTRL+Q or CTRL+\) sends SIGQUIT
  1. Processes: Other processes can send signals from one process to anther by calling the kill function. One restriction is that both process must be owned by the same owner or the sender must be super-user.

Signal Names

Every Signal has a name.

  • They all begin with SIG.
  • Signal names are defined by positive integer constants in signal.h. or sys/signal.h.
  • No signal has a value 0.
SIGABRT Generated by calling the abort function
SIGALRM Generated when a timer expired that was set with the alarm function
SIGCHLD Child process terminates or stops
SIGFPE Any arithmetic exception such as divide by 0
SIGINT Generated by terminal driver when we type CTRL+C
SIGQUIT Generated by terminal driver when we type CTRL+\
SIGKILL Allows sys admin to terminate a process
SIGSEGV Process has made an invalid memory reference
SIGTERM Sent by kill command by default
SIGTRAP Often used by implementations to transfer control to a debugger when a breakpoint is reached.
SIGUSR1 User defined signal
SIGUSR2 User defined signal
SIGXCPU Process has exceeded its soft CPU time limit.  (OSX ignores, Linux terminates)

Signal Dispositions

When a program starts up, each signal is assigned a disposition: the action that occurs when it receives the signal.  The disposition can be the kernel’s default action, an action different than the kernel’s default action, or to execute a signal handler.

  • A child process inherits its parent’s signal dispositions since it runs in the same address space.
  • When a process calls exec, the new memory space does not have access to the parent’s signal handlers (if any), so new handers need to be established (if any).
  • If signal handers are to be established they should be done immediately after the process begins running so uncaught signals do not occur.

Below are a list of signals and default actions.

Name Description ISO C Default Action
SIGABRT abnormal Termination * terminate + core
SIGALRM timer expired terminate
SIGCHLD Child process terminate ignore
SIGFPE floating point exception * terminate + core
SIGINT terminal interrupt character * terminate
SIGQUIT Terminal quit character terminate + core
SIGKILL termination terminate
SIGSEGV invalid memory reference * terminate + core
SIGTERM termination * terminate
SIGTRAP hardware fault terminate + core
SIGUSR1 User defined signal terminate
SIGUSR2 User defined signal terminate
SIGXCPU CPU limit exceeded terminate + core

Handling Signals

When a program is running and it is sent a signal the following occurs:

  • The kernel sets a flag in the process’ state space.
  • If the kernel is in an uninterruptable system call or the intended process is blocking the signal,
    • the signal is put in a pending queue.
    • If the signal is blocked, it remains in a pending queue until the signal is either unblocked or the disposition is changed to ignore.
  • Else
    • the signal is delivered to the process and the process’ normal execution is temporarily halted and the signal handler (either default or set by the process) is run. After the signal handler is run, the process continues where it left off.

Note: UNIX systems divide system calls into two categories, slow (that can block forever – like waiting for keyboard input, pause, wait, etc.) and the others. Linux and OSX stop slow system calls and restart them after the signal is handled.

What if when the signal was delivered, the process was in the process of allocating memory with malloc?  And what if the signal handler called malloc?  Since malloc keeps a linked list of all of the memory that was allocated, if the process was in the middle of changing that list, then that list could be corrupted.

Unix provides a list of async-signal safe functions that can be used in signal handlers.  These functions don’t use static or global data and don’t call malloc or free.

Most system functions use errno for return values.  When using system functions in signal handlers we should first save errno, use the system functions, then reset errno before exiting the signal handler.

© 2017, Eric. All rights reserved.