mirror of
https://github.com/PDP-10/its.git
synced 2026-01-13 15:27:28 +00:00
Binary-only compiler and library, plus documentation and include files for compiling new programs.
241 lines
10 KiB
Plaintext
Executable File
241 lines
10 KiB
Plaintext
Executable File
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.
|
||
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.
|
||
A "core" file is never made, since this is unnecessary and unhelpful
|
||
on TOPS-20/TENEX.
|
||
|
||
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.
|
||
|
||
These interrupt characters can only be some form of control character,
|
||
including DEL.
|
||
|
||
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.
|