3 minute read

Exception and Interrupt

Before we talk about signals, lets go over interrupts and exceptions briefly. The operating system maintains a table of exceptions and interrupts, the entry of each of which corresponds to a handler that gets called when the system gets a specific exception/interrupt. Exceptions and interrupts are inplemented in the hardware and the handlers are maintained by the operating system.

But these two things don’t seem to be enough. What if a process is running and the user hits ctrl-c? How does the process know that it needs to be terminated? So we need some kinds of mechanism for the process to know and act upon some specifi events.


A signal is a small message that notifies a process that an event of some type has occurred in the system.

  • akin to exceptions and interrupts (like a software version of exception/interrupt)
  • sent from the kernal (sometimes at the request of another process) to a process
  • signal type is identified by small integer ID’s (1-30)
  • only info in a signal is its ID and the fact that it arrived

Sending a Signal

  • Kernal sends (delivers) a signal to a destination process by updating some state in the context of the destination process (flip a bit in the process)
  • Kernal sends a signal for one of the following reasons:
    • Kernal has detected a system event such as divide-by-zero (SIGFPE) or the terminal of a child process (SIGCHLD). SIGCHLD is used to implement background process handling for the shell
    • Another process has invoked the kill system call explicitly request the kernal to send a signal to the destination process (kill means sending signals)

Sending a signal means flipping a bit corresponding to a signal in a process (pending signal mask). If multiple signals are sent, the bit stays on. So a flipped bit means one or more signals is received. It doesn’t cause the process to do actions reponding to the signal; it just delivers a message that a signal is received. A process acts upon a signal upon return to user mode from supervisor mode. When the process gets back to user mode, it walks through the signal table; if one of the bits is set, it clears the bit and calles the corresponding signal handler.

Receiving a Signal

A destination process receives a signal when it is forced by the kernal to react in some way to the delivery of the signal

Three possible ways to react:

  • Ignore the signal (reset the bit and do nothing)
  • Terminate the process (with optional core dump)
  • Catch the signal by executing a user-level function called signal handler
    • Akin to a hardware exception handler being called in response to an asynchronous interrupt

Pending and Blocked Signals

A signal is pending if sent but not yet received (not yet act upon it)

  • There can be at most one pending signal of any particular type (only one bit)

A process can block the receipt of certain signals

  • Blocked signals can be delivered, but will not be received until the signal is unblocked

Process Data Structures

So for each process, there are 3 corresponding data structures:

  • An array of signal handlers
  • Pending signal mask
  • Blocking signal mask

So the process will act upon a signal if the pending signal mask is on and blocking signal mask is off.

Process Groups

Every process belongs to exactly one process group There is exactly group leader of each group, which is the first process that is put into the group. It is nothing special except that its pid is the groupid. Each process group has only one input and one output. There can be only one foregroup process group and many background process groups for each session, which is an abstraction that associated with a text input device (keyboard) and a text output device (screen). That means only one process group can interact with the keyboard.

Some Examples of Sending Signals

So /bin/kill is the program that can send arbitrary signal to a process or process group, e.g. (negative pid is groupid) Typing ctrl-c/ctrl-z sends a SIGINT/SIGTSTP to every job in the foreground process group

There are default actions corresponding to each signals, but we can write our own signal handler but implementing the signal function

handler_t *signal(int signum, handler_t *handler)