According to POSIX definitions,
3.28 Asynchronously-Generated Signal
A signal that is not attributable to a specific thread. Examples are signals sent via kill(), signals sent from the keyboard, and signals delivered to process groups. Being asynchronous is a property of how the signal was generated and not a property of the signal number. All signals may be generated asynchronously.
Then,
3.379 Synchronously-Generated Signal
A signal that is attributable to a specific thread.
For example, a thread executing an illegal instruction or touching invalid memory causes a synchronously-generated signal. Being synchronous is a property of how the signal was generated and not a property of the signal number.
If an illegal instruction causes a synchronously-generated signal, how may it be generated asynchronously?
For example, say I have a program that runs two threads, A and B. Now, suppose an illegal instruction takes place in A and causes a signal SIGILL to be raised. Is a POSIX-compliant system required to invoke the signal handler defined for SIGILL in thread A? Or is it allowed to interrupt
thread B and invoke that signal handler in B?
Related:
List of Synchronous and Asynchronous Linux/Posix Signals
The two scenarios are different, and 'synchronous' vs. 'asynchronous' is determined at the time of generation.
To rephrase your second question a bit, you ask, "can a synchronously-generated SIGILL attributed to a particular thread be delivered to a different thread in the same process?"
No. A synchronously-generated signal can only be delivered to the thread that caused it. (2.4.1 Signal Generation and Delivery)
Now as a caveat and as mentioned in another answer, normal signal masking semantics do not apply to some synchronously-generated signals. In particular, as the specs pthread_sigmask and sigprocmask say, "[i]f any of the SIGFPE, SIGILL, SIGSEGV, or SIGBUS signals are generated while they are blocked, the result is undefined, unless the signal was generated [asynchronously]". (emphasis added)
Yes, you can send SIGILL,SIGSEGV,SIGBUS,SIGFPE,... to processes via kill(2), or a specific thread via pthread_kill(3). Masking only applies to kill generated signals, so if you install a handler for SIGILL, and mask it, your handler will only be invoked for real illegal instruction traps.
Related
I want to have a signal handler on the fatal signals that default to dumping core that will log the ocurrence and then the core will still be dumped (unless disabled with ulimit or core pattern).
I have tested (on Linux 4.15) that if the signal handler simply returns, this is what happens. However, I have not found any explicit statement in the documentation that would clearly state this.
So is it defined, in POSIX or Linux documentation, what shall happen when the signal handler returns, and where?
I did the first test by tweaking the code I needed to make work and it was more convoluted then I thought. When I tested with simple example program, the only way that works for all cases is to reset the handler and re-raise the signal as described in the accepted answer.
Core files have a definition in POSIX.1-2017 XBD (3.117 Core File):
A file of unspecified format that may be generated when a process terminates abnormally.
POSIX.1-2017 XSH (2.4.3 Signal Actions, under SIG_DFL) contains the following text (with any emphasized part from here on meaning that the corresponding text in the standard is XSI-shaded):
If the default action is to terminate the process abnormally, the process is terminated as if by a call to _exit(), except that the status made available to wait(), waitid(), and waitpid() indicates abnormal termination by the signal.
If the default action is to terminate the process abnormally with additional actions, implementation-defined abnormal termination actions, such as creation of a core file, may also occur.
In XBD (13. Headers, under <signal.h>) we see SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGQUIT, SIGSEGV, SIGSYS, SIGTRAP, SIGXCPU and SIGXFSZ tagged as
A -- Abnormal termination of the process with additional actions.
So from a POSIX perspective you can't rely on a core file being generated, irrespective of signal dispositions.
However, every signal with a default action of "A" in POSIX is listed with a default disposition of "Core" in the Linux manual (signal(7)). That may be what the following excerpt of the manual about SIGSYS, SIGXCPU and SIGXFSZ refers to:
Linux 2.4 conforms to the POSIX.1-2001 requirements for these signals, terminating the process with a core dump.
As the POSIX excerpts above tell us, it's not a hard requirement in POSIX.1-2017.
Now that still doesn't answer the question if registering a signal-catching function nullifies the signal action of abnormal termination. I believe that if it does, it results in undefined behavior for at least a few signals, as per the following paragraph from XSH (2.4.3 Signal Actions, under Pointer to a Function):
The behavior of a process is undefined after it returns normally from a signal-catching function for a SIGBUS, SIGFPE, SIGILL, or SIGSEGV signal that was not generated by kill(), sigqueue(), or raise().
So to avoid UB in all cases, I believe you have to reset the signal disposition to SIG_DFL and then re-raise() the signal from within the signal handler anyway. Also, any handlers catching those signals should probably run on an alternate signal stack but I'm not quite sure if that would make it generally safe to do so and if it is in the first place.
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.
I am not new to the use of signals in programming. I mostly work in C/C++ and Python.
But I am interested in knowing how signals are actually implemented in Linux (or Windows).
Does the OS check after each CPU instruction in a signal descriptor table if there are any registered signals left to process? Or is the process manager/scheduler responsible for this?
As signal are asynchronous, is it true that a CPU instruction interrupts before it complete?
The OS definitely does not process each and every instruction. No way. Too slow.
When the CPU encounters a problem (like division by 0, access to a restricted resource or a memory location that's not backed up by physical memory), it generates a special kind of interrupt, called an exception (not to be confused with C++/Java/etc high level language exception abstract).
The OS handles these exceptions. If it's so desired and if it's possible, it can reflect an exception back into the process from which it originated. The so-called Structured Exception Handling (SEH) in Windows is this kind of reflection. C signals should be implemented using the same mechanism.
On the systems I'm familiar with (although I can't see why it should be much different elsewhere), signal delivery is done when the process returns from the kernel to user mode.
Let's consider the one cpu case first. There are three sources of signals:
the process sends a signal to itself
another process sends the signal
an interrupt handler (network, disk, usb, etc) causes a signal to be sent
In all those cases the target process is not running in userland, but in kernel mode. Either through a system call, or through a context switch (since the other process couldn't send a signal unless our target process isn't running), or through an interrupt handler. So signal delivery is a simple matter of checking if there are any signals to be delivered just before returning to userland from kernel mode.
In the multi cpu case if the target process is running on another cpu it's just a matter of sending an interrupt to the cpu it's running on. The interrupt does nothing other than force the other cpu to go into kernel mode and back so that signal processing can be done on the way back.
A process can send signal to another process. process can register its own signal handler to handle the signal. SIGKILL and SIGSTOP are two signals which can not be captured.
When process executes signal handler, it blocks the same signal, That means, when signal handler is in execution, if another same signal arrives, it will not invoke the signal handler [ called blocking the signal], but it makes the note that the signal has arrived [ ie: pending signal]. once the already running signal handler is executed, then the pending signal is handled. If you do not want to run the pending signal, then you can IGNORE the signal.
The problem in the above concept is:
Assume the following:
process A has registered signal handler for SIGUSR1.
1) process A gets signal SIGUSR1, and executes signalhandler()
2) process A gets SIGUSR1,
3) process A gets SIGUSR1,
4) process A gets SIGUSR1,
When step (2) occurs, is it made as 'pending signal'. Ie; it needs to be served.
And when the step (3) occors, it is just ignored as, there is only one bit
available to indicate the pending signal for each available signals.
To avoid such problem, ie: if we dont want to loose the signals, then we can use
real time signals.
2) Signals are executed synchronously,
Eg.,
1) process is executing in the middle of signal handler for SIGUSR1,
2) Now, it gets another signal SIGUSR2,
3) It stops the SIGUSR1, and continues with SIGUSR2,
and once it is done with SIGUSR2, then it continues with SIGUSR1.
3) IMHO, what i remember about checking if there are any signal has arrived to the process is:
1) When context switch happens.
Hope this helps to some extend.
When segmentation fault occurs on Linux within multithreaded application and handler is called, are all other threads instantly stopped before handler is called?
So, is it appropriate to rely on fact that no any parralel code will execute during segmentation fault handling?
Thank you.
From the signal(7) manual page:
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.
This paragraph says that certain signals, like SIGSEGV, are thread specific. Which should answer your question.
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.