OSDN Git Service

update some documentation
authorcgf <cgf>
Sat, 6 Mar 2004 21:43:57 +0000 (21:43 +0000)
committercgf <cgf>
Sat, 6 Mar 2004 21:43:57 +0000 (21:43 +0000)
winsup/cygwin/how-signals-work.txt

index 26e5ee1..9a2b478 100644 (file)
-Copyright 2001, 2002, 2003 Red Hat Inc., Christopher Faylor
+Copyright 2001, 2002, 2003, 2004 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).
+[note that the following discussion is still incomplete]
 
-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.
+How do signals work?
 
-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.
+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 '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 function 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 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.
-
-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
+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" to it.  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' 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.
+
+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.
+
+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.
-
-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.
+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() 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().