114 lines
6.0 KiB
Plaintext
114 lines
6.0 KiB
Plaintext
Copyright 2001, 2002, 2003 Red Hat Inc., Christopher Faylor
|
|
|
|
[this information is currently out-of-date]
|
|
How do signals work?
|
|
|
|
On process startup, cygwin starts a secondary thread that deals with signals.
|
|
This thread contains a loop which blocks waiting for information to show up
|
|
on a pipe whose handle (sendsig) is currently stored in _pinfo (this may change).
|
|
|
|
Communication on the sendsig pipe is via the 'sigelem' structure. This
|
|
structure is filled out by the sig_send function with information about the
|
|
signal being sent, such as (as of this writing) the signal number, the
|
|
originating pid, the originating thread, and the address of the mask to
|
|
use (this may change).
|
|
|
|
If the signal is not blocked, then the function "sig_handle" is called
|
|
with the signal number as an argument. This is a fairly straightforward
|
|
function. It first checks to see if the signal is special in any way.
|
|
|
|
A special signal is something like SIGKILL or SIGSTOP. The user has no
|
|
control over how those signals affect a UNIX process. If a SIGKILL is
|
|
received then sig_handle calls exit_sig to exit the process. If SIGSTOP
|
|
is called then sig_handle calls the regular signal dispatch function
|
|
with a special function argument "sig_handle_tty_stop". The signal
|
|
dispatch function is described below.
|
|
|
|
An uncaught signal like SIGTERM or SIGHUP will cause the process to exit
|
|
with the standard UNIX exit values. Uncaught signals like SIGUSR1 are
|
|
ignored, as on UNIX.
|
|
|
|
If the signal has an associated signal handler, then the setup_handler
|
|
function is eventually called. It is passed the signal, the address of
|
|
the handler, and a standard UNIX sigaction structure. The meat of
|
|
signal processing is in setup_handler.
|
|
|
|
setup_handler has a "simple" task. It tries to stop the appropriate
|
|
thread and redirect its execution to the signal handler function.
|
|
Currently, the "appropriate thread" is only the main thread. Someday
|
|
we'll have to change this to allow cygwin to interrupt other user
|
|
threads.
|
|
|
|
To accomplish its task, setup_handler first inspects the static sigsave
|
|
structure. This structure contains information on any not-yet-handled
|
|
signals that may have been set up by a previous call to setup_handler
|
|
but not yet dispatched in the main thread. If the sigsave structure
|
|
seems to be "active", then a "pending" flag is set (see below) and the
|
|
function returns. Otherwise processing continues.
|
|
|
|
After determining that sigsave is available, setup_handler will take one
|
|
of two routes, depending on whether the main thread is executing in the
|
|
cygwin DLL or is currently in "user" code. We'll discuss the cygwin DLL
|
|
case first.
|
|
|
|
If sigsave seems to be available, then the frame information for the
|
|
main thread is inspected. This information is set by any cygwin
|
|
function that is known to block (such as _read()), usually by calling
|
|
'sigframe thisframe (mainthread)' in the cygwin function. This call
|
|
sets up information about the current stack frame of an executing cygwin
|
|
process. Any function which uses 'sigframe thisframe' should be signal
|
|
aware. It should detect when a signal has arrived and return
|
|
immediately. This method is also used throughout the DLL to ensure
|
|
accurate frame info for the executing function. So, you'll see it
|
|
sprinkled liberally throughout the DLL, usually at places where
|
|
empirical tests have indicated problems finding this address via the
|
|
brute force method stack walking method employed in setup_handler.
|
|
|
|
So, if mainframe is active, that means that we have good information
|
|
about the state of the main thread. Cygwin uses the stack frame info
|
|
from this structure to insert a call to the assembly language function
|
|
'sigdelayed' in place of the main thread's normal return address. So,
|
|
when a call to (e.g.) _read returns after detecting a signal, it does
|
|
not return to its caller. Rather, it returns to sigdelayed.
|
|
|
|
The sigdelayed function saves a lot of state on the stack and sets the
|
|
signal mask as appropriate for POSIX. It uses information from the
|
|
sigsave structure which has been filled in by interrupt_on_return, as
|
|
called by setup_handler. sigdelayed pushes a "call" to the function
|
|
"sigreturn" on the stack. This will be the return address seen by the
|
|
signal handler. After setting up the return value, modifying the signal
|
|
mask, and saving other information on the stack, sigreturn clears the
|
|
sigsave structure (so that setup_handler can use it) and jumps to the
|
|
signal handler function. And, so a UNIX signal handler function is
|
|
emulated.
|
|
|
|
The signal handler function operates as normal for UNIX but, upon
|
|
return, it does not go directly back to the return address of the
|
|
original cygwin function. Instead it returns to the previously
|
|
mentioned 'sigreturn' assembly language function.
|
|
|
|
sigreturn resets the process mask to its state prior to calling the
|
|
signal handler. It checks to see if any new signals have come in and
|
|
calls the handler for them now, ensuring that the order of signal
|
|
arrival is more or less maintained. It checks to see if a cygwin
|
|
routine has set a special "restore this errno on returning from a
|
|
signal" value and sets errno to this, if so. Finally, it restores all
|
|
of the register values that were in effect when sigdelayed was called.
|
|
|
|
Ok, you thought I had forgotten about the 'pending' stuff didn't you?
|
|
Well, if you can rewind up to the discussion of sig_handle, we'll return
|
|
to the situation where sigsave was currently active. In this case,
|
|
setup_handler will set a "pending" flag, will reincrement the appropriate
|
|
element of the above signal array, and will return 0 to indicate that
|
|
the interrupt did not occur. Otherwise setup_handler returns 1.
|
|
|
|
For pending signals, the theory is that the signal handler thread will
|
|
be forced to be rerun by having some strategic cygwin function call
|
|
sig_send with a __SIGFLUSH "argument" to it. This causes the signal
|
|
handler to rescan the signal array looking for pending signals.
|
|
|
|
This leads us to the sig_send function. This is the "client side" part
|
|
of the signal manipulation process. sig_send is the low-level function
|
|
called by a high level process like kill(). You would use sig_send
|
|
to send a __SIGFLUSH to the signal thread.
|