33

If a Unix (Posix) process receives a signal, a signal handler will run.

What will happen to it in a multithreaded process? Which thread receives the signal?

In my opinion, the signal API should be extended to handle that (i.e. the thread of the signal handler should be able to be determined), but hunting for infos on the net I only found year long flames on the linux kernel mailing list and on different forums. As I understood, Linus' concept differed from the Posix standard, and first some compat layer was built, but now the Linux follows the posix model.

What is the current state?

2
  • 3
    Duplicate of stackoverflow.com/questions/11679568/… "pthreads(7) describes that POSIX.1 requires all threads in a process share attributes, including signal dispositions"
    – steve
    Aug 26, 2015 at 19:17
  • @steve Thanks, but 1) it is on another SE site 2) this spec doesn't specify clearly, what will exactly happen. What it means, the signal handlers will be called on all threads, but it seems a little bit surrealistic to me. 3) That answer doesn't specify what was Linus' model and why/how is it used currently.
    – peterh
    Aug 26, 2015 at 19:52

2 Answers 2

20

The entry in POSIX on "Signal Generation and Delivery" in "Rationale: System Interfaces General Information" says

Signals generated for a process are delivered to only one thread. Thus, if more than one thread is eligible to receive a signal, one has to be chosen. The choice of threads is left entirely up to the implementation both to allow the widest possible range of conforming implementations and to give implementations the freedom to deliver the signal to the "easiest possible" thread should there be differences in ease of delivery between different threads.

From the signal(7) manual on a Linux system:

A signal may be generated (and thus pending) for a process as a whole (e.g., when sent using kill(2)) or for a specific thread (e.g., certain signals, such as SIGSEGV and SIGFPE, generated as a consequence of executing a specific machine-language instruction are thread directed, as are signals targeted at a specific thread using pthread_kill(3)). A process-directed signal may be delivered to any one of the threads that does not currently have the signal blocked. If more than one of the threads has the signal unblocked, then the kernel chooses an arbitrary thread to which to deliver the signal.

And in pthreads(7):

Threads have distinct alternate signal stack settings. However, a new thread's alternate signal stack settings are copied from the thread that created it, so that the threads initially share an alternate signal stack (fixed in kernel 2.6.16).

From the pthreads(3) manual on an OpenBSD system (as an example of an alternate approach):

Signals handlers are normally run on the stack of the currently executing thread.

(I'm currently not aware of how this is handled when multiple threads are executing concurrently on a multi-processor machine)

The older LinuxThread implementation of POSIX threads only allowed distinct single threads to be targeted by signals. From pthreads(7) on a Linux system:

LinuxThreads does not support the notion of process-directed signals: signals may be sent only to specific threads.

1
  • 1
    Given the "(fixed in kernel 2.6.16)", the docs suggest that the current situation is that: Threads have distinct alternate signal stack settings, and a new thread starts with no alternate signal stack defined. So by default, when a pthread_kill signal comes in for a thread, the process-wide signal handler is executed on top of that thread's stack. Is this correct?
    – nh2
    Mar 19, 2020 at 12:55
10

Extending the accepted answer, there is a more practical view, what I found here.

The essence is the following:

Signal handlers are per-process, but signal masks are per-thread.

  1. Thus, if we install/uninstall a signal handler (with signal() or sigaction()) on any thread, it will affect all of them.
  2. If a process gets a signal, the handler will be executed only on a single thread. This thread is pseudo-randomly selected among them, whose signal mask accepts it. My experiments show that it is always the thread with the least pid.*
  3. Signals sent to any thread are considered as signal sent to the main process. Thus, if a thread gets a signal, it is quite possible that an other thread will execute the handler. Best if we see that as if threads (identified by tids, thread ids) would be considered as masked processes (identified by pids), and signals sent to a tid would be forwarded to their pid.
  4. For the execution of a signal handler, in its signal mask the given signal number is automatically masked. This is to prevent stacked signal handler execution in a signal burst. This can be changed with the SA_NODEFER flag of the sigaction(...) call.
  5. (3) and (4) results that in the case of a signal burst, the system distributes the signal handlers possibly most parallelly.
  6. However, if we have set up the sigaction with SA_NODEFER, always the same thread will get the signal and they will stack.

*Comment says it might by also the thread created first. Both would match the posix standard, so do not trust it in your code.

3
  • Thanks for the follow up. (Yes, I know this is a year after the answer) I am wondering about your experiments. Is it thread with the least pid, because that's the first thread of the group to be started or have you also tested a case where pid overflows. I'm thinking it would be the pid of first created thread, and maybe it's also possible to change this (by changing session leader? something else?)
    – domen
    Apr 29, 2021 at 14:40
  • @domen Thanks. If there is no SA_NODEFER, then any thread can get the signal whose sigmask allows it. Note, the lack of SA_NODEFER causes that the signal is masked on the receiving thread until the signal handler exits, so no handler will be called twice. If there is SA_NODEFER, this auto-masking won't happen. The posix standard does not specify, which thread will get the signal, so it is up to Linus, how does it work. I do not know, that always the firstly created process or the process with the least pid gets the handler, both would match the posix standard.
    – peterh
    Apr 29, 2021 at 14:44
  • @doman But you can check it, cause a pid overflow by opening many threads with a small /proc/sys/kernel/pid_max value. Or by turning on some pid randomization.
    – peterh
    Apr 29, 2021 at 14:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.