How are asynchronous signals handled in Linux? - linux

This seems like a silly question, but I can't find the answer to it anywhere I look. I know that in UNIX, signals are handled asynchronously. If I write a function that handles a signal, where is that function run? Is a new thread spawned? Is an existing thread interrupted somehow? Or is this handled in a system thread like asynchronous I/O is?

A signal function is executed as if a thread in the process has been interrupted. That is, the signal handler is called using the signaled thread and the stack is rearranged so that when the signal handler returns the thread continues execution. No new threads are introduced.

An existing process thread is interrupted until the function returns. There are serious restrictions on what it can safely do to ensure it doesn't corrupt state of function calls the thread was in the middle of - specifically, any functions it calls that the thread may have already been calling must be async reentrant. See the man pages e.g. signal, sigaction for further details or ask more specific questions as you like.

It's not a separate thread, but your code is hastily suspended. That's why only a limited subset of the POSIX calls is available.
From the signal man page:
The routine handler must be very careful, since processing elsewhere was interrupted at some arbitrary point. POSIX has the concept of "safe function". If a signal interrupts an unsafe function, and handler calls an unsafe function, then the behavior is undefined. Safe functions are listed explicitly in the various standards.

Related

How to log signals to an application signals with a log handler?

There are ways to do some work with linux signal handlers.
We can either register system handlers for every signals (if we have sourcecode) or
Run the process under strace to view them.
Stratergy 1:
But if we dont have source code, how can we catch a signals to an application to do something with it and return back? (not a one time debugging but permanent feature). [may be hack a system call?]
Stratergy 2:
And in case we do have source code, is writing to a file safe in case of multiple signals ? or is it more safe to execute signal handler in a fork() process and discard SIGCHLD? what happens if another signals comes in when handling previous signal?
For your Stratergy 2, depends on how your log files are written and how the signals are triggered (asynchronously or not). Normally stdio library functions are not async-signal-safe.
See details in http://man7.org/linux/man-pages/man7/signal-safety.7.html
To avoid problems with unsafe functions, there are two possible
choices:
1. Ensure that (a) the signal handler calls only async-signal-safe
functions, and (b) the signal handler itself is reentrant with
respect to global variables in the main program.
2. Block signal delivery in the main program when calling functions
that are unsafe or operating on global data that is also accessed
by the signal handler.
Stratergy 1: But if we dont have source code, how can we catch a signals to an application to do something with it and return back? (not a one time debugging but permanent feature). [may be hack a system call?]
To intercept a signal delivered to a process there are at least 2 ways:
ptrace(2) (which is what strace uses) see this answer for an example.
LD_PRELOAD: (I'd not advise this approach) you can use it to set handlers for every signal and replace signal and sigaction with two wrapper functions to prevent the program from overriding your signal handlers (please note the recommendations in this other answer).

what does it actually mean when they say "fragile state" in this context?

Because signals are asynchronous, the main program may be in a very
fragile state when a signal is processed and thus while a signal
handler function executes. Therefore, you should avoid performing any
I/O operations or calling most library and system functions from
signal handlers
I want to do a test under this circumstance,but I don't know how to turn the main program into fragile state
reference: Advanced Linux Programming
I don't think "fragile state" is a technical term. I'm not intimately familiar with how the Linux lernel processes signals, but it's possible that the kernel disables (or lacks) certain safeguards while performing signal processing, such as thread safety on global objects or not expecting other side-effects.
For example, your quotation specifically mentions IO operations. An IO operation is typically asynchronous and causes an interrupt to be raised when it completes, I expect the kernel does not want an IO interrupt handler to be invoked while it is processing a signal (which I imagine would preempt the currently running code in the signal handler) and potentially put the system into an undefined state if the interrupt handler modified some resource that the signal handler was using, especially if the signal being handled was for a process that was also waiting on an IO operation.

when multi-thread program receive a SIGPIPE signal because send, which thread would handle the signal in linux?

If a send cause a SIGPIPE signal, which thead would handle it ? The thread which send or a random thread? In other words, the Linux system send the signal by kill or pthread_kill?
Asynchronous signals like SIGPIPE can go to any thread. You can use signal masks to limit which of the threads is eligible.
Synchronous signals like SIGSEGV will be delivered on the thread that caused them.
Summary
The answer to this question has two facets: How the system in question should behave and how it actually behaves.
Since most programmers expect Linux to be mostly POSIX-compatible, we can look into that standard, which actually unambiguously specifies the behavior – the signal is sent directly to the thread which did the write. But whether Linux adheres to it is unclear and Linux documentation is not helpful here. An examination of Linux behavior suggests it conforms to POSIX, but doesn't prove it, and a reading of the source gives us the necessary proof about the current version of Linux.
tl;dr: It is always handled by the thread that did the write.
POSIX Standard
The POSIX standard mandates (since IEEE Std. 1003.1-2001/Cor 2-2004) that SIGPIPE generated as a result of write to a pipe with no readers be delivered to the thread doing the write. See EPIPE in the ERRORS section of the description of write() (emphasis mine):
[EPIPE] An attempt is made to write to a pipe or FIFO that is not open for reading by any process, or that only has one end open. A SIGPIPE signal shall also be sent to the thread.
Linux documentation
That said, it is not clear whether Linux handles this correctly. The page man 7 signal doesn't give concrete lists of thread- and process-directed signals, just examples, and its definition of thread-directed signals doesn't include SIGPIPE:
A signal may be thread-directed because it was generated as a consequence of executing a specific machine-language instruction that triggered a hardware exception […]
SIGPIPE is not a result of a specific instruction, nor is it triggered by a hardware exception.
Glibc documentation doesn't discuss kernel-generated synchronous thread-directed signals at all (i.e. not even SIGSEGV or SIGBUS are discussed as being thread-directed), and there are years-old reports of bugs in NPTL, although these may have been fixed in the meantime.
Observable Linux behavior
I wrote a program which spawns a thread, which blocks SIGPIPE using pthread_sigmask, creates a pipe pair, closes the read end and writes a byte into the write end. If the signal is thread-directed, nothing should happen until the signal is unblocked again. If the signal is process-directed, the main thread should handle the signal and the process should die. The reason for this again comes from POSIX: If there is a thread which has the (process-directed) signal unblocked, it should be delivered there instead of queueing:
Signals generated for the process shall be delivered to exactly one of those threads within the process which […] has not blocked delivery of the signal. If […] all threads within the process block delivery of the signal, the signal shall remain pending on the process until […] a thread unblocks delivery of the signal, or the action associated with the signal is set to ignore the signal.
My experimentation suggests that on modern (2020) Linux with recent Glibc the signal is indeed directed to the thread which did the write, because blocking it with pthread_sigmask in the writing thread prevents SIGPIPE from being delivered until it's unblocked.
Linux 5.4.28 source
The behavior observed above doesn't prove anything, because it is entirely possible that Linux simply violates POSIX in several places and the signal delivery depends on some factors I didn't take into account. To get the proof we seek, we can read the source. Of course, this only tells us about the current behavior, not about the intended one – but if we find the current behavior to be POSIX-conforming, it is probably here to stay.
Disclaimer: I'm not a kernel hacker and the following is a result of a cursory reading of the sources. I might have missed something important.
In kernel/signal.c, there is a SYNCHRONOUS_MASK listing the synchronous signals which are handled specially. These are SIGSEGV, SIGBUS, SIGILL, SIGTRAP, SIGFPE and SIGSYS – SIGPIPE is not in the list. However, that doesn't answer the question – it can be thread-directed without being synchronous.
So how is SIGPIPE sent? It originates from pipe_write() in fs/pipe.c, which calls send_sig() on task_struct current. The use of current already hints that the signal is thread-directed, but let's press on. The send_sig() function is defined in kernel/signal.c and through some indirection ultimately calls __send_signal() with pid_type type = PIDTYPE_PID.
In Linux terminology, PID refers to a single thread. And sure enough, with those parameters, the pending signal list is the thread-specific one, not the shared one; and complete_signal() (called at the end of the function) doesn't even try to find a thread to wake up, it just returns because the thread has already been chosen. I don't fully understand how the signal queues work, but it seems that the queue is per-thread and so the current thread is the one that gets the signal.

Accessing shared data from a signal handler

I want to know if it is a good idea to access shared data from a signal handler. I mean consider the scenario of multi process system and multithreaded system with a single process. In multi process system, lets say I have the processes handle a particular signal and update certain shared variable or memory by the processes. Can I do that from the signal handler itself.
However, in the case of threads using pthreads, I don't think it is doable. http://maxim.int.ru/bookshelf/PthreadsProgram/htm/r_40.html. As given in this article, they have mentioned that it is not asynchronous signal safe and have suggested to use sigwait for that. I am not why it is not asynchronous signal safe. I mean lets say, I handle a signal by a thread and is in the signal handler routing. I acquire a lock on the shared memory to update it. In the mean time another signal of the same type arrives and another thread responsible for handling it executes the signal handler again. Here the signal handler is same for the process but it is called multiple time. The second time around, it cannot see the lock and updates/overrides the data. Is this the issue with multithreaded signal handlers using shared data.
I am a bit confused, in multi process systems, I have a copy of the signal handler for each process. But in multithreaded system, there is a single copy of the signal handler used by the multiple threads isn't it. So when multiple signals of the same type arrives and we have two threads that are responsible for handling it try to handle it, then both of them will try to execute the same piece of handler code? How does it all fit in?
I read through the article that you reference and found some interesting information in the "Threads in Signal Handlers" section. In that section, you'll see that they have a list of Posix function calls that can be made from within signal handlers. Then soon after that list, they mention the following:
But where are the Pthreads calls? They're not in either of these
lists! In fact, the Pthreads standard specifies that the behavior of
all Pthreads functions is undefined when the function is called from a
signal handler. If your handler needs to manipulate data that is
shared with other threads≈buffers, flags, or state variables≈it's out
of luck. The Pthreads mutex and condition variable synchronization
calls are off limits.
Notice the last sentence: "Pthreads mutex and condition variable synchronization calls are off limits"
The aforementioned functions that can be called from a signal handler are described as follows:
These functions have a special property known as reentrancy that
allows a process to have multiple calls to these functions in progress
at the same time.
The pthread synchronization functions dont have the special property known as reentrancy, so I imagine that if these functions (pthread_mutex_lock() for instance) are interrupted by an arriving signal, then the behavior is not "safe".
Imagine that your application calls pthread_mutex_lock(&theMutex) and at exactly that moment (that is, while in the pthread_mutex_lock() function) a signal arrives. If the signal handler also calls pthread_mutex_lock(&theMutex), the previous pthread call may not have terminated, so it cant be guaranteed which call to pthread_mutex_lock() will get the lock. So the resulting behavior will be undefined/undeterministic.
I would imagine that the call to sigwait() from a particular thread would guarantee that no important, non-reentrancy function calls may get interrupted, thus allowing calls to the pthread synchronization functions to be "safe".

Linux/vxworks signals

I came across the following in a vxworks manual and was wondering why this is the case.
What types of things do signals do that make them undesirable?
In applications, signals are most
appropriate for error and exception
handling, and not for a
general-purpose inter-task
communication.
The main issue with signals is that signal handlers are registered on a per process/memory space basis (in vxWorks, the kernel represents one memory space, and each RTP is a different memory space).
This means that regardless of the thread/task context, the same signal handler will get executed (for a given process). This can cause some problems with side-effects if your signal handler is not well behaved.
For example, if your signal uses a mutex for protect a shared resource, this could cause nasty problems, or at least, unexpected behavior
Task A Task B Signal Handler
Take Mutex
...
Gets preempted
does something
....
<SIGNAL ARRIVES>----->Take Mutex (blocks)
resumes
....
Give Mutex
----->Resumes Handler
I'm not sure the example above really conveys what I'm trying to.
Here are some other characteristics of signals:
Handler not executed until the task/process is scheduled. Just because you sent the signal, doesn't mean the handler will execute right away
No guarantee on which Task/Thread will execute the handler. Any thread/task in the process could run it (whichever thread/task executes first). VxWorks has ways around this.
Note that the above only applies to asynchronous signals sent via a kill call.
An exception will generate a synchronous signal which WILL get executed right away in the current context.

Resources