Google
 

Trailing-Edge - PDP-10 Archives - SRI_NIC_PERM_FS_1_19910112 - c/lib5/usys/signal.doc
There are 9 other files named signal.doc in the archive. Click here to see a list.
		KCC UN*X SIGNAL SIMULATION

	This file provides some user-oriented information on how to
use the KCC simulation of the Un*x signal mechanism.

	Unfortunately, there is no single consistent signal mechanism
used by all UN*X-type systems.  The currently known schemes fall into
three basic categories:

	(1) Traditional: V7, SYS V, ANSI. [signal()]
	(2) Better: 4.2 BSD / SUN	  [signal(), sigvec(), sig*()]
  KCC=>	(3) Best:   4.3 BSD		  [signal(), sigvec(), sig*()]

Attributes:
		Handler reset	Signal mask	Calls restarted
	(1)	yes		no		no
	(2)	no		yes		no
  KCC=>	(3)	no		yes		yes

Handler Reset:
	In traditional Un*x implementations, the call of a signal handler
automatically resets that signal's handler to SIG_DFL (default handling,
normally termination).  The handler has to do invoke signal() again if
it wishes to catch additional signals.  4.3BSD and KCC do not do this
reset.

Signal mask:
	BSD introduced a signal mask which allows signals to be kept
pending until the mask no longer blocks them from being handled.
Whenever a handler is called, the corresponding bit in its mask (at
least) is always set; thus there is no need to reset the handler to
SIG_DFL.  This is much more robust as there is no gap during which
quickly repeated asynchronous signals can mistakenly kill a process.
	BSD added a number of new calls to handle this mask.  They are
sigvec (general-purpose replacement for signal), sigsetmask, sigblock, and
sigpause.  KCC implements all of these.

Call restart:
	In both (1) and (2) a signal during certain system calls would
cause those calls to return -1 with errno set to EINTR.  In 4.3BSD this
was changed so that normally such calls are restarted automatically when
a signal handler returns.  A flag bit with sigvec() allows the old action
of EINTR to still be taken.  A new call, sigreturn(), was added to permit
true context restoration.  KCC implements this too.

More on system call interruption:
	On Un*x signals may only interrupt the following calls:
		read(), write()	- slow devices only (never DSK:)
		wait()
		ioctl() on a slow device (esp. TTY:)
		any call that locks an inode - open(), creat()?
	Calls which are not restarted by 4.3BSD if interrupted:
		pause(), sigpause()

	KCC permits only the above USYS calls to be interrupted.  For all
but pause() and sigpause(), the calls will be restarted automatically
unless specifically requested by the SV_INTERRUPT bit in a sigvec call.

Changing the signal mechanism:
	To accomodate cases where it is difficult to change the user
code (e.g. during initial stages of porting some software), the signal
mechanism can be changed from 4.3BSD to 4.2BSD or V7/SYSV by including
the following code in the module where "main" is defined:

	#define _URTSUD_DEFAULT_SIGS _URTSUD_xxx_SIGS
	#include <urtsud.h>

where "xxx" is one of SYSV, BSD42, or BSD43.

For additional information more detailed than that provided in this
file, consult the files CODSIG.DOC and SIGVEC.C in the source directory.
KCC-supported signals:

		/--------------- (A)synchronous or (S)ynchronous.
		|  /------------ (P)anic channel or not.
		|  |        /--- Only seen for (J)SYS or (U)ser; * = both.
	Code: [AS][P-][O-][JU*]
			\------- O means if interrupt PC is user-mode, the
				PC is that of the offending instruction and
				not the next one (as for all other cases).

Signal		PSI	Code

SIGINT		x	A--*	TTY Interrupt (interactive)
SIGQUIT		x	A--*	TTY Quit (interactive)
SIGALRM		x	A--*	Alarm Clock (TIMER%)

SIGCHLD		.ICIFT	A--*	Inferior fork termination
SIGFPE		.ICFOV	S--U	Floating Point overflow
SIGFPE		.ICAOV	S--U	Arithmetic overflow
SIGSEGV		.ICPOV	SP-U	PDL overflow
SIGILL		.ICILI	SP-*	Illegal Instruction
SIGBUS		.ICIRD	SPO*	Illegal memory read
SIGBUS		.ICIWR	SPO*	Illegal memory write
SIGPIPE		.ICDAE	SPO*	Device or data error
SIGXFSZ		.ICQTA	SPO*	Quota exceeded
SIGXFSZ		.ICMSE	SPO*	Machine resources exhausted

SIGT20EOF	.ICEOF	S--J	EOF condition on input
SIGT20NXP	.ICNXP	S-O*	Ref to non-ex page

	If a panic signal occurs during execution of a USYS call then
the program will be halted with an error message, even if a handler is
defined for that signal.  It is possible to ignore panic signals with
SIG_IGN although this is unwise.


KCC-Unsupported signals:

	All remaining signals which do not have a direct PSI
equivalent are lumped together and use a "miscellaneous" PSI channel.
It is only possible for these signals to be triggered if the user
program does an explicit raise(sig).  Note that kill(getpid(),sig) is the
same as raise(sig).
Default signal handling:

	All C programs start with the PSI interrupt system turned off,
and it is left off to reduce overhead unless the program makes an
explicit call to one of the signal functions (signal, sigvec,
sigblock, etc).  This means that a PSI which happens prior to such an
initializing call will receive whatever the default TOPS-20 action is
for that interrupt.

	Once the signal facilities are initialized and the PSI system
turned on, the default action (SIG_DFL) for a particular signal
varies.  As long as a signal is not set to anything, its action
remains whatever the TOPS-20 system action is; panic signals will
cause the process to be halted, and all other signals are ignored.  If
a signal is explicitly set to SIG_DFL then its default action will
become whatever the Un*x default action is.  See the include file
<signal.h> for a listing of all signals with their default actions.

	There are five possible actions documented in the UPM
for a signal on Un*x.  Their corresponding equivalents on TOPS-20/TENEX are:

	UN*X			TOPS-20
(1) Ignore			Ignores signal if possible
(2) Terminate			Halts with error message
(3) Terminate with core dump	Halts with error message
(4) Stop (suspend)		Does HALTF% quietly
(5) Call handler		Calls handler

	A "core" file is never made, since this is unnecessary and unhelpful
on TOPS-20/TENEX.
Process control with signals:

	SIGTSTP and SIGCONT are partially supported.  The recommended,
BSD-portable way for an inferior process to return temporarily to its
superior is by doing:

	kill(getpid(), SIGTSTP);	/* or raise(SIGTSTP) */

This will stop the process with a HALTF%.  If it is continued, a SIGCONT
signal will automatically be generated, and will invoke a handler if any
exists.

	It is possible for a program to use kill() to send signals to
its parent (superior) or its children (inferiors); the PID argument to
kill() should be that returned from a previous getpid() or fork().
This ability is limited however to just those signals which are
"supported" with specific PSI channels; the "miscellaneous" signals
cannot be triggered in another process.

	The signals SIGKILL, SIGSTOP, SIGTSTP, and SIGCONT are interpreted
specially when applied to a child process.  SIGKILL does a HFORK% to halt
the inferior; SIGSTOP and SIGTSTP do a FFORK% to freeze it; and SIGCONT
does a RFORK% to resume execution of a frozen child.

	Note that SIGCONT cannot be fully implemented because TOPS-20
does not provide any general way for a process to know whether it has
been halted and continued.  The current implementation does the best
that can be done, which is to artificially generate SIGCONT after
being continued from an explicit triggering of SIGTSTP.

	SIGTSTP cannot at this time be generated by TTY input.  If there
is a need for it this may be possible.
Use of SIGINT and SIGQUIT:

	At startup there are no interrupt characters.  That is,
t_intrc and t_quitc of the "tchars" ioctl structure are both -1.
Simply setting these characters does not cause either to generate
signals unless the signal handler has been explicitly set to something
by signal().  If explicitly set to SIG_DFL then the signal will
terminate the process, since this is the default Un*x action.
	The best method of using these TTY interrupt characters is to
first declare the action you wish taken (using signal() or sigvec()),
and then using a TIOCSETC ioctl() to set the characters to the desired
values.  These interrupt characters can only be some form of control
character, including DEL; TOPS-20 does not permit non-controls to be used.
Signal Handlers:

	When a signal handler is invoked, it is called with the following
arguments:
	void sighandler(sig, chn, scp)
	int sig;			/* Signal # */
	int chn;			/* PSI channel # (T20/10X) */
	struct sigcontext *scp;		/* Pointer to context */

Since more than one PSI channel is mapped into a single signal, the
"chn" variable allows the handler to distinguish between them if
necessary.  The signal context structure is defined in <signal.h> and
contains the complete context of the signal, including the interrupt
PC and flags, saved ACs, and old signal mask.  Code which references
this structure is not portable to machines other than the PDP-10, but
at this level things are non-portable anyway.

	Returning from the handler will resume the process where it was
interrupted.  longjmp() to some other location will work as long as the
signal handlers are not nested.

WARNING:
	Any time you write a signal handler routine, you must be aware
of what you might be interrupting and how the handler actions may
affect the rest of the program.  There are too many subtleties to go
into more than a few of the important aspects here.  For example,
while it should be okay to use USYS calls within the handler (the
interrupt system ensures that these are treated as atomic), it is
almost NEVER okay to invoke any library routine which alters static
data, such as "ctime".  In particular, none of the storage allocation
facilities such as "malloc" should be called, because the program might
have been interrupted out of a call to malloc, and the data structures
will be in an inconsistent state.  It is also risky to use any of
the standard I/O library routines, for similar reasons.

WARNING:
	The signal code goes to a great deal of trouble to ensure that
if a user program JSYS is interrupted, it can be continued on return
from the handler.  But the TOPS-20/TENEX PSI scheme is so complex and
messed up that it is impossible to guarantee that this will always
work.  To be COMPLETELY safe, you can use the jsys() facility, which will
never permit its JSYS to be interrupted unless the JSYS_OKINT flag is
set, and even then will provide you with a definite indication that
a signal was handled.
Additional notes:

What "a/synchronous" means:

	A SYNCHRONOUS interrupt is one that happens at a specific PC
due to the instruction at that location.  Typical examples: illegal
instruction interrupts (which can include JSYS calls), floating-point
exceptions, and illegal memory references.  For these types of
interrupts the PC is significant and it or the contents it points to
may need to be checked to determine what to do, because simply
continuing the process at that PC will very likely just generate
another such interrupt.

	An ASYNCHRONOUS interrupt is one that may happen at ANY time,
regardless of place; these are generated by events external to the
program.  Typical examples: TTY input interrupts, timer interrupts.
For these, the PC is unimportant except that it should be preserved
and restored if the interrupt handler wishes to continue whatever was
interrupted.

	No UN*X C signal handler has the capability of returning from
handling a synchronous interrupt.  In fact there is no mechanism
provided for a signal handler to find out what its return PC is.
(it's possible, with trickery, but I've never seen an example).
4.3BSD (as opposed to 4.2 or any other Un*x) now makes this possible
by providing the handler with a pointer to a saved-context structure!

	Note that some signal handlers return to normal code by
means of longjmp(); this is particularly true for alarm() handlers.
ANSI specifies that longjmp should restore the environment properly
even from within a signal handler, but is not required to do anything
meaningful if called from a nested signal handler.  KCC supports this
use of longjmp().
Extensions to sigvec():

	For additional flexibility, the "sigvec" structure has been
extended to include additional parameters.  Some new flags in sv_flags
are used to indicate when the additional structure members are
significant.

The things that can be specified, independently of each other:
	SV_XINTLEV: ON  If handler should run at interrupt level.
	SV_XASMHAN: ON  If handler is special assembler routine (ACs not saved,
			no args given).  Otherwise, normal C handler.
	SV_XOS:	    ON	If the sigvec structure should be checked for:
		(1) Exact PSI channel # to use for this signal (0 = existing).
		(2) What PSI level to use (0 = existing).
		(3) .TICxx code (plus 1) to ATI% to this channel (0 = none).

Not all of the flags work yet:

	Currently only SV_XINTLEV is implemented.  It works to use
longjmp() within handlers called with this flag!

	SV_XASMHAN is not yet used.  If added, it will be an error to
specify SV_XASMHAN without SV_XINTLEV; that is, if the handler is an
assembly routine, then it MUST run at interrupt level.

	SV_XOS is not yet used.  If added, specification of a positive
.TIC code will always replace any existing code by the new one, and
use of -1 will always clear any existing code.  If the value is 0,
however, then the meaning depends on whether a channel # was
specified.  If the channel # was given, 0 is the same as -1.
Otherwise, if no channel # was given, then 0 means leave any existing
code alone.