📄 how-signals-work.txt
字号:
Copyright 2001 Red Hat Inc., Christopher FaylorHow do signals work?On process startup, cygwin starts a secondary thread that deals with signals.This thread contains a loop which blocks waiting for one of three events:1) sigcatch_main - a semaphore which, when incremented, indicates that a signal may be available for the main thread. The caller waits for the signal to be delivered before returning.2) sigcatch_nonmain - a semaphore which , when incremented, indicates that a signal is available for a non-main thread (currently this is not truly implemented). The caller waits for the signal to be delivered before returning.3) sigcatch_nosync - a semaphore which, when incremented, indicates that a signal may be available for the main thread. The caller does not wait for the delivery of the signal before returning.So, the signal handler blocks waiting for one of these three semaphores.If one of these is activated, then the the signal handler inspects anarray of integers looking for a non-zero value. The array correspondsto the normal UNIX signals + two extra locations for internal usage.This array is located in the 'sigtodo' array in the procinfo class.The signal thread uses the InterlockedDecrement function to atomicallyinspect elements of the array. If one one of the elements of the arrayis non-zero, then cygwin checks to see if the user has blocked thesignal by inspecting the process signal mask. If the signal is blocked,then the current array element is reincremented and the next element ischecked.If the signal is not blocked, then the function "sig_handle" is calledwith the signal number as an argument. This is a fairly straightforwardfunction. 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 nocontrol over how those signals affect a UNIX process. If a SIGKILL isreceived then sig_handle calls exit_sig to exit the process. If SIGSTOPis called then sig_handle calls the regular signal dispatch functionwith a special function argument "sig_handle_tty_stop". The signaldispatch function is described below.An uncaught signal like SIGTERM or SIGHUP will cause the process to exitwith the standard UNIX exit values. Uncaught signals like SIGUSR1 areignored, as on UNIX.If the signal has an associated signal handler, then the setup_handlerfunction is eventually called. It is passed the signal, the address ofthe handler, and a standard UNIX sigaction structure. The meat ofsignal processing is in setup_handler.setup_handler has a "simple" task. It tries to stop the appropriatethread and redirect its execution to the signal handler function.Currently, the "appropriate thread" is only the main thread. Somedaywe'll have to change this to allow cygwin to interrupt other userthreads.To accomplish its task, setup_handler first inspects the static sigsavestructure. This structure contains information on any not-yet-handledsignals that may have been set up by a previous call to setup_handlerbut not yet dispatched in the main thread. If the sigsave structureseems to be "active", then a "pending" flag is set (see below) and thefunction returns. Otherwise processing continues.After determining that sigsave is available, setup_handler will take oneof two routes, depending on whether the main thread is executing in thecygwin DLL or is currently in "user" code. We'll discuss the cygwin DLLcase first.If sigsave seems to be available, then the frame information for themain thread is inspected. This information is set by any cygwinfunction that is known to block (such as _read()), usually by calling'sigframe thisframe (mainthread)' in the cygwin function. This callsets up information about the current stack frame of an executing cygwinprocess. Any function which uses 'sigframe thisframe' should be signalaware. It should detect when a signal has arrived and returnimmediately. This method is also used throughout the DLL to ensureaccurate frame info for the executing function. So, you'll see itsprinkled liberally throughout the DLL, usually at places whereempirical tests have indicated problems finding this address via thebrute force method stack walking method employed in setup_handler.So, if mainframe is active, that means that we have good informationabout the state of the main thread. Cygwin uses the stack frame infofrom 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 doesnot return to its caller. Rather, it returns to sigdelayed.The sigdelayed function saves a lot of state on the stack and sets thesignal mask as appropriate for POSIX. It uses information from thesigsave structure which has been filled in by interrupt_on_return, ascalled by setup_handler. sigdelayed pushes a "call" to the function"sigreturn" on the stack. This will be the return address seen by thesignal handler. After setting up the return value, modifying the signalmask, and saving other information on the stack, sigreturn clears thesigsave structure (so that setup_handler can use it) and jumps to thesignal handler function. And, so a UNIX signal handler function isemulated.The signal handler function operates as normal for UNIX but, uponreturn, it does not go directly back to the return address of theoriginal cygwin function. Instead it returns to the previouslymentioned 'sigreturn' assembly language function.sigreturn resets the process mask to its state prior to calling thesignal handler. It checks to see if any new signals have come in andcalls the handler for them now, ensuring that the order of signalarrival is more or less maintained. It checks to see if a cygwinroutine has set a special "restore this errno on returning from asignal" value and sets errno to this, if so. Finally, it restores allof 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 returnto the situation where sigsave was currently active. In this case,setup_handler will set a "pending" flag, will reincrement the appropriateelement of the above signal array, and will return 0 to indicate thatthe interrupt did not occur. Otherwise setup_handler returns 1.For pending signals, the theory is that the signal handler thread willbe forced to be rerun by having some strategic cygwin function callsig_send with a __SIGFLUSH "argument" to it. This causes the signalhandler to rescan the signal array looking for pending signals.This leads us to the sig_send function. This is the "client side" partof the signal manipulation process. sig_send is the low-level functioncalled by a high level process like kill(). You would use sig_sendto send a __SIGFLUSH to the signal thread.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -