is execution of signal handler un-preemptible in linux?
Asked Answered
O

3

6

I have a process p registered with a signal handler for SIGALRM. A timer is setup to periodically send signal SIGALRM to process p. There are also multiple threads running in process p. Is the signal handler, when being triggered and executed, un-preemptible? Or to say, is it that the execution of signal handler will not be interrupted by any thread in process p?

PS: I thought signal handler is executed in kernel (is it?) and kernel is unpreemptive to user-mode threads? Correct me if it's wrong...

Oxfordshire answered 25/5, 2011 at 17:40 Comment(2)
It can be pre-empted - there is nothing particularly special about signal handler code.Planula
A kernel is pre-emptive. It has always something to do.Purdy
C
16

Pretty much - don't - dealing with shared data in a signal handler almost always leads to a world of pain, dealing with threads as well and you got yourself a mess.

By default a signal is blocked while the signal handler is running (at least on linux, that might not be universally true), so at least the signal handler will not be preempted by itself. Though, if you have multiple threads, and the signal is not blocked in the other threads, a signal handler might very well be run concurrently within several threads.

One thread will receive the signal and execute the handler, it's more or less random which thread that'll be, although you could control it by blocking the signal in all threads you don't want to handle the signal.

However, any of the other threads bar the one handling the signal might run in parallell. The thread handling a signal could run the signal handler at pretty much any point in the program (as long as the signal isn't blocked.) So, you'd need some sort of locking to protect that data. The problem is you can't use any of the normal thread locking primitives, they are not signal async safe. Meaning if you e.g. try to grab a pthread_mutex_t in a signal handler, you easily deadlock your program.

The only functions you can safely call in a signal handler are the ones listed here . With regards to protecting the shared data, you could use sigblock()/sigunblock() as a sort of protection, ensuring the signal handler doesn't run while you're accessing that shared data - and the signal have to blocked in all the threads, otherwise it'll just run within one of the threads that doesn't have it blocked - going down that road is madness.

Pretty much the only shared data you can safely access in a signal handler is a sig_atomic_t type, in practice other kinds of primitive types usually safe too.

What you really should do in a signal handler is just

  • set a global flag
  • check that flag elsewhere in code when suitable, and take action

Or

  • have some sort of main loop that monitors file descriptors for events using select()/poll() or similar.
  • create a pipe and monitor that in your main loop
  • write() a byte to a pipe in your signal handler
  • run your code to deal with the signal, including protecting any shared data when the mainloop detects an event on that pipe

Or

  • Keep a spare thread around
  • block the given signal in all your threads
  • have that spare thread loop on calling sigsuspend() with a signal mask ensuring delivery of that signal.
  • run your code, including protecting any shared data to deal with the signal when sigsuspend() returns
Cabasset answered 25/5, 2011 at 18:23 Comment(1)
Excellent explanation! You have covered most of the pitfalls of using signals along with multi-thread application along with the possible solutions! Thanks for the detailed explanation.Kensell
Q
5

Is the signal handler, when being triggered and executed, un-preemptible?

No, the signal handler is preemptive like any other user level function.

I thought signal handler is executed in kernel (is it?)

No, the signal handler is not executed in kernel mode.

Kernel checks for pending signals for process while switching from kernel mode to user mode. If it finds a pending signal, it setup user's stack frame such that after returning to user mode, the process starts executing the signal handler.Thereafter process starts executing in user mode executing the signal handler like any other user level function. When the execution is completed, process is switched to kernel mode. Kernel then restore the original context of process, executing before the time of signal handling.
All this mode switching is not magic. Kernel change the appropriate return address in user stack.

Quadrilateral answered 26/5, 2011 at 12:28 Comment(0)
M
2

The short answer is "no".

Read up on sigaction, especially the sa_mask field. By default, your thread can be interrupted by another signal even while it is in a signal handler.

Also, the phrase "interrupted by any thread in process p" is not meaningful. In general, threads run concurrently; they do not "interrupt" each other (except by calling pthread_kill()).

Mcwherter answered 25/5, 2011 at 18:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.