update some documentation
This commit is contained in:
parent
f2afcfa616
commit
9514a64249
|
@ -1,84 +1,107 @@
|
|||
Copyright 2001, 2002, 2003 Red Hat Inc., Christopher Faylor
|
||||
Copyright 2001, 2002, 2003, 2004 Red Hat Inc., Christopher Faylor
|
||||
|
||||
[note that the following discussion is still incomplete]
|
||||
|
||||
[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).
|
||||
On process startup, cygwin starts a secondary thread which 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).
|
||||
Communication on the sendsig pipe is via the 'sigpacket' 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.
|
||||
Any cygwin function which calls a win32 api function is wrapped by the
|
||||
assembly functions "_sigfe" and "_sigbe". These functions maintain a
|
||||
cygwin "signal stack" which is used by the signal thread to control
|
||||
handling of signal interrupts. Cygwin functions which need to be
|
||||
wrapped by these functions (the majority) are labelled by the SIGFE
|
||||
option in the file cygwin.din.
|
||||
|
||||
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.
|
||||
The cygwin.din function is translated into a standard cygwin.def file by
|
||||
the perl script "gendef". This function notices exported cygwin
|
||||
functions which are labelled as SIGFE and generates a front end assembly
|
||||
file "sigfe.s" which contains the wrapper glue necessary for every
|
||||
function to call sigfe prior to actually dispatching to the real cygwin
|
||||
function. This generated function contains low-level signal related
|
||||
functions: _sigfe, _sigbe, sigdelayed, sigreturn, longjmp, and setjmp.
|
||||
|
||||
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.
|
||||
The signal stack maintained by sigfe/sigbe and friends is a secondary
|
||||
shadow stack. Addresses from this stack are swapped into the "real"
|
||||
stack as needed to control program flow. The intent is that executing
|
||||
cygwin functions will still see roughly the same stack layout and will
|
||||
be able to retrieve arguments from the stack but will always return
|
||||
to the _sigbe routine so that any signal handlers will be properly
|
||||
called.
|
||||
|
||||
Upon receipt of a "non-special" (see below) signal, the function
|
||||
sigpacket::process is called. This function determines what action, if
|
||||
any, to take on the signal. Possible actions are: Ignore the signal (e.g.,
|
||||
SIGUSR1), terminate the program (SIGKILL, SIGTERM), stop the program
|
||||
(SIGSTOP, SIGTSTP, etc.), wake up a sigwait or sigwaitinfo in a
|
||||
targetted thread, or call a signal handler (possibly in a thread).
|
||||
If no thread information has been sent to sigpacket::process, it determines
|
||||
the correct thread to use based on various heuristics, as per UNIX.
|
||||
Signals sent via the UNIX kill() function are normally sent to the
|
||||
main thread. Ditto signals sent as the result of pressing tty keys,
|
||||
like CTRL-C.
|
||||
|
||||
Signals which stop a process are handled by a special internal handler:
|
||||
sig_handle_tty_stop. Some signals (e.g., SIGKILL, SIGSTOP) are
|
||||
uncatchable, 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.
|
||||
the handler, a standard UNIX sigaction structure, and a pointer to the
|
||||
thread's "_cygtls" information. 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.
|
||||
thread and either redirect its execution to the signal handler function,
|
||||
flag that a signal has been received (sigwait) or both (sigpause).
|
||||
|
||||
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.
|
||||
To accomplish its task, setup_handler first inspects the target thread's
|
||||
local storage (_cygtls) 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 target thread. If this
|
||||
structure seems to be "active", then setup_handler returns, notifying it's
|
||||
parent via a false value. 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.
|
||||
(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.)
|
||||
|
||||
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.
|
||||
After determining that it's ok to send a signal, setup_handler will lock
|
||||
the cygtls stack to ensure that it has complete access. It will then
|
||||
inspect the thread's 'incyg' element. If this is true, the thread is
|
||||
currently executing a cygwin function. If it is false, the thread is
|
||||
unlocked and it is assumed that the thread is executing "user" code.
|
||||
The actions taken by setup_handler differ based on whether the program
|
||||
is executing a cygwin routine or not.
|
||||
|
||||
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.
|
||||
If the program is executing a cygwin routine, then the
|
||||
interrupt_on_return function is called which sets the address of the
|
||||
'sigdelayed' function is pushed onto the thread's signal stack, and the
|
||||
signal's mask and handler is saved in the tls structure. Then the
|
||||
'signal_arrived' event is signalled, as well as any thread-specific wait
|
||||
event.
|
||||
|
||||
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
|
||||
Since the sigdelayed function was saved on the thread's signal stack,
|
||||
when the cygwin functio returns, it will eventually return to the
|
||||
sigdelayed "front end". The sigdelayed function will save a lot of
|
||||
state on the stack and set the signal mask as appropriate for POSIX.
|
||||
It uses information from the _cygtls structure which has been filled in
|
||||
by interrupt_setup, as called by setup_handler. sigdelayed pushes a
|
||||
"call" to the function "sigreturn" on the thread's signal stack. This
|
||||
will be the return address eventually 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 signal number in the
|
||||
_cygtls structure so that setup_handler can use it and jumps to the
|
||||
signal handler function. And, so a UNIX signal handler function is
|
||||
emulated.
|
||||
|
||||
|
@ -88,26 +111,43 @@ 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.
|
||||
signal handler. 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. It pops the signal stack, places the new return address on
|
||||
the real stack, restores all of the register values that were in effect
|
||||
when sigdelayed was called, and then returns.
|
||||
|
||||
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.
|
||||
Ok. That is more or less how cygwin interrupts a process which is
|
||||
executing a cygwin function. We are almost ready to talk about how
|
||||
cygwin interrupts user code but there is one more thing to talk about:
|
||||
SA_RESTART.
|
||||
|
||||
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.
|
||||
UNIX allows some blocking functions to be interrupted by a signal
|
||||
handler and then return to blocking. In cygwin, so far, only
|
||||
read/readv() operate in this fashion. To accommodate this behavior,
|
||||
readv notices when a signal comes in and then calls the _cygtls function
|
||||
'call_signal_handler_now'. 'call_signal_handler_now' emulates the
|
||||
behavior of both sigdelayed and sigreturn. It sets the appropriate
|
||||
masks and calls the handler, returning true to the caller if SA_RESTART
|
||||
is active. If SA_RESTART is active, readv will loop. Otherwise
|
||||
it will return -1 and set the errno to EINTR.
|
||||
|
||||
Phew. So, now we turn to the case where cygwin needs to interrupt the
|
||||
program when it is not executing a cygwin function. In this scenario,
|
||||
we rely on the win32 "SuspendThread" function. Cygwin will suspend the
|
||||
thread using this function and then inspect the location at which the
|
||||
thread is executing using the win32 "GetThreadContext" call. In theory,
|
||||
the program should not be executing in a win32 api since attempts to
|
||||
suspend a process executing a win32 call can cause disastrous results,
|
||||
especially on Win9x.
|
||||
|
||||
If the process is executing in an unsafe location then setup_handler
|
||||
will return false as in the case above. Otherwise, the current location
|
||||
of the thread is pushed on the thread's signal stack and the thread is
|
||||
redirected to the sigdelayed function via the win32 "SetThreadContext"
|
||||
call. Then the thread is restarted using the win32 "ResumeThread" call
|
||||
and things proceed as per the sigdelayed discussion above.
|
||||
|
||||
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.
|
||||
called by a high level process like kill() or pthread_kill().
|
||||
|
|
Loading…
Reference in New Issue