Skip to content

Signals, Part 2: Pending Signals and Signal Masks

angrave edited this page Nov 21, 2014 · 19 revisions

How can I learn more about signals?

The linux man pages discusses signal system calls in section 2. There is also a longer article in section 7:

man -s7 signal

What is a process's signal disposition?

For each process, each signal has a disposition which means what action will occur when a signal is delivered to the process. For example, the default disposition SIGINT is to terminate it. However this disposition can be changed by calling signal() (or as we will learn later) sigaction() to install a signal handler for a particular signal. You can imagine the processes' disposition to all possible signals as a table of function pointers entries (one for each possible signal).

The default disposition for signals can be to ignore the signal, stop the process, continue a stopped process, terminate the process, or terminate the process and also dump a 'core' file. Note a core file is a representation of the processes' memory state that can be inspected using a debugger.

Can multiple signals be queued?

No - however it is possible to have signals that are in a pending state. If a signal is pending it means it has not yet been delivered to the process. The most common reason for a signal to be pending is that the process (or thread) has currently blocked that particular signal.

If a particular signal, e.g. SIGINT, is pending then it is not possible to queue up the same signal again.

It is possible to have more than one signal of a different type in a pending state. For example SIGINT and SIGTERM signals may be pending (i.e. not yet delivered to the target process)

How do I block signals?

Signals can be blocked (meaning they will stay in the pending state) by setting the process signal mask or, when you are writing a multi-threaded program, the thread signal mask.

What happens when creating a new thread?

The new thread inherits a copy of the calling thread's mask

pthread_sigmask(...) // set my mask to block delivery of some signals pthread_create( ... ) // new thread will start with a copy of the same mask

What happens when forking?

The child process inherits a copy of the parent's signal dispositions. In other words, if you have installed a SIGINT handler before forking, then the child process will also call the handler if a SIGINT is delivered to the child.

Note pending signals for the child are not inherited during forking.

What happens during exec ?

pending signals; disposition ; Todo

Under construction ...

How do I block signals in a single-threaded program?

Use sigprocmask! With sigblockmask you can set the new mask, add new signals to be blocked to the process mask, and unblock currently blocked signals. You can also determine the existing mask (and use it for later) by passing in a non-null value for oldset.

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);`

From the Linux man page of sigprocmask,

SIG_BLOCK The  set  of blocked signals is the union of the current set and the set argument.
SIG_UNBLOCK
              The signals in set are removed from the current set of blocked  signals.
              It is permissible to attempt to unblock a signal which is not blocked.
SIG_SETMASK
              The set of blocked signals is set to the argument set.

The sigset type behaves as a bitmap, except functions are used rather than explicitly setting and unsetting bits using & and |.

It is a common error to forget to initialize the signal set before modifying one bit. For example,

sigset_t set , oldset;
sigaddset(&set, SIGINT); // Ooops!
sigprocmask(SIG_SETMASK, &set, &oldset)

Correct code initializes the set to be all on or all off. For example,

sigfillset(&set); // all signals
sigprocmask(SIG_SETMASK, &set, NULL); // Block all the signals!
// (Actually SIGKILL or SIGSTOP cannot be blocked...)

sigemptyset(&set); // no signals 
sigprocmask(SIG_SETMASK, &set, NULL); // set the mask to be empty again

How do I block signals in a multi-threaded program?

Blocking signals is similar in multi-threaded programs to single-threaded programs:

  • Use pthread_sigmask instead of sigprocmask
  • Block a signal in all threads to prevent its asynchronous delivery

The easiest method to ensure a signal is blocked in all threads is to set the signal mask in the main thread before new threads are created

sigemptyset(&set);
sigaddset(&set, SIGQUIT);
sigaddset(&set, SIGINT);
pthread_sigmask(SIG_BLOCK, &set, NULL);

// this thread and the new thread will block SIGQUIT and SIGINT
pthread_create(&thread_id, NULL, myfunc, funcparam);

How are pending signals delivered in a multi-threaded program?

A signal is delivered to any signal thread that is not blocking that signal.

If the two or more threads can receive the signal then which thread will be interrupted is arbitrary!

Clone this wiki locally