| Contributed by 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 ** |