159 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			159 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
Copyright 2001, 2002, 2003, 2004 Red Hat Inc., Christopher Faylor
 | 
						|
 | 
						|
[note that the following discussion is still incomplete]
 | 
						|
 | 
						|
How do signals work?
 | 
						|
 | 
						|
On process startup, cygwin starts a secondary thread which deals with
 | 
						|
signals.  This thread contains a loop which blocks waiting for
 | 
						|
information to arrive on a pipe whose handle (sendsig) is currently
 | 
						|
stored in _pinfo (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).
 | 
						|
 | 
						|
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.
 | 
						|
 | 
						|
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 file contains low-level signal related
 | 
						|
functions: _sigfe, _sigbe, sigdelayed, sigreturn, longjmp, and setjmp.
 | 
						|
 | 
						|
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 the same stack layout as if they had
 | 
						|
been called directly 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.  As
 | 
						|
per linux, the only time a handler is called in a thread is when there
 | 
						|
is some kind of fault like SIGSEGV, SIGILL, etc.  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, 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 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 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.
 | 
						|
 | 
						|
(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.  This causes the signal handler to
 | 
						|
rescan the signal array looking for pending signals.)
 | 
						|
 | 
						|
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' boolean.  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.
 | 
						|
 | 
						|
If the program is executing a cygwin routine, then the
 | 
						|
interrupt_on_return function is called which causes the address of the
 | 
						|
'sigdelayed' function to be pushed onto the thread's signal stack, and
 | 
						|
the signal's mask and handler to be saved in the tls structure.  After
 | 
						|
performing these operations, the 'signal_arrived' event is signalled, as
 | 
						|
well as any thread-specific wait event.
 | 
						|
 | 
						|
Since the sigdelayed function was saved on the thread's signal stack,
 | 
						|
when the cygwin function 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.
 | 
						|
 | 
						|
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 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.  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.
 | 
						|
 | 
						|
UNIX allows some blocking functions to be interrupted by a signal
 | 
						|
handler and then return to blocking.  In cygwin, so far, only
 | 
						|
read/readv() and the wait* functions operate in this fashion.  To
 | 
						|
accommodate this behavior, a function 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, the function will loop.  Otherwise it will typically 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 (quickly!) 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() or pthread_kill().
 | 
						|
 | 
						|
** More to come **
 |