A Simple Way to Interrupt System Calls Under 4.2 BSD
donn at sdchema.UUCP
donn at sdchema.UUCP
Wed Dec 14 21:28:54 AEST 1983
Apparently some people are having trouble with the new 4.2 BSD signal
mechanism because it doesn't interrupt certain system calls. Suppose
you have arranged to catch interrupt signals, and your program does a
read on a terminal and waits for someone to type in some data. Now
suppose a person at the terminal hits the interrupt key: your signal
handling routine is called and returns. What happens? Instead of
aborting the read, it is restarted. Under the old signal mechanism, we
expect the read to immediately return -1 with an 'interrupted system
call' error.
It would be nice to have the ability to cause system calls to be
aborted the old way. Some people on the net have made some radical
suggestions about how this might be done:
A nice clean way to handle this would be to have the
signal-handling routine's return code indicate whether the
system call which was interrupted, if any, should be
restarted. This would still require changes to old code which
doesn't return anything meaningful from the signal handler.
Maybe yet another flag, specified in the call to signal that
gives the handler's address?
-- dmmartindale at watcgl.UUCP
What we need here is an ioctl (fnctl) to set/clear the 'system
call restart' feature, just like the 'close on exec' feature.
-- Clyde W. Hoover @ Univ. of Texas Computation Center
But I think it should be possible to get the proper effect without
making even more kernel changes. One change that works is to use
select() instead of read(), since select() may be interrupted. (This
was suggested by steveh at hammer.)
Another possibility is to read the manual page on the new signals and
figure out how restarted system calls are implemented. I tried this
and came up with a loathsome but interesting hack that appears to make
interrupted reads a snap. The trick is to realize that one of the
parameters supplied to a signal handler is a pointer to a record of the
context in which the signal occurred. This record contains the PC
where the process is to be restarted after the signal catcher returns.
For a restartable system call, this points at the system call itself:
when the signal handler exits, the PC is set to the same CHMK
instruction that initiated the system call previously, and the system
call is repeated. To return elsewhere, one merely diddles this stored PC.
The following is a working C routine that diddles the stored PC so that
the system call appears to return immediately with a value of -1 and
error number set to 'interrupted system call':
---------------------------------------------------------------------------
/*
* causeintr.c
*
* Function: Allow signals to interrupt system calls.
* Usage: causeintr( scp )
* struct sigcontext *scp;
* Date: Wed Dec 14 03:35:06 PST 1983
* Author: Donn Seeley, UC San Diego
* Remarks:
* This routine is strictly for use with the new 4.2 BSD signal
* mechanism, and has gross VAX dependencies. It is completely
* and utterly non-portable and thus represents a gorgeous hack.
*
* This routine must be compiled without using the '-O' optimizer,
* which will strip out the 'unreachable' error code given a chance.
*
* The situation in which this routine is used is the following:
* You desire to use the new, modern, etc. signal mechanism in 4.2
* but you require some of your signal handlers to have the ability
* to interrupt a system call. A handler which needs to interrupt
* a system call should be declared
*
* handler( sig, code, scp )
* int sig;
* int code;
* struct sigcontext *scp;
*
* and should call 'causeintr( scp )' before returning. If a
* slow system call was being processed when the signal was
* received, the system call will appear to return immediately
* with a return value of -1 and EINTR (interrupted system call)
* will be the error number in 'errno'.
*/
# include <signal.h>
# include <errno.h>
/*
* CHMK is the change-mode-to-kernel opcode which all system calls use.
*/
# define CHMK 0xbc
extern int errno;
int
causeintr( scp )
struct sigcontext *scp;
{
/*
* Hack, hack.
*/
union {
int (*fi_func)();
int fi_int;
} fi;
/*
* This goto is used to make 'error' appear at a fixed (known)
* offset from the beginning of the routine. (Gag, splutter)
*/
goto normal;
error:
/*
* Fake a return from an interrupted system call.
*/
errno = EINTR;
return ( -1 );
normal:
/*
* Make sure that we are supposed to return to a system call.
* If so, then patch sc_pc so that we return to 'error' instead!
*/
if ( ((* (char *) scp->sc_pc) & 0xff) == CHMK ) {
fi.fi_func = causeintr;
scp->sc_pc = fi.fi_int + 0x6; /* Magic number */
}
return ( 0 );
}
---------------------------------------------------------------------------
I even have a working program which contains an example of the use
of this routine:
---------------------------------------------------------------------------
/*
* sigtest.c
*
* Trivial program to test 4.2 BSD signal handling.
*/
# include <stdio.h>
# include <signal.h>
# include <errno.h>
# define STDIN 0
# define STDOUT 1
# define STDERR 2
# define mask(x) (1 << (x))
extern int dosig();
extern int errno;
struct sigvec sv, osv;
char buf[256];
main()
{
register int i;
int m;
sv.sv_handler = dosig;
sv.sv_mask = mask( SIGINT ) | mask( SIGQUIT ) | mask( SIGTSTP );
sv.sv_onstack = 0;
sigvec( SIGINT, &sv, &osv );
sigvec( SIGQUIT, &sv, &osv );
sigvec( SIGTSTP, &sv, &osv );
do {
errno = 0;
i = read( STDIN, buf, sizeof buf );
if ( i > 0 )
fprintf( stderr, "Data: %.*s", i, buf );
else if ( i == 0 )
fprintf( stderr, "End of file\n" );
else
perror( "Error" );
} while ( i != 0 );
exit( 0 );
}
dosig( sig, code, scp )
unsigned int sig;
int code;
struct sigcontext *scp;
{
psignal( sig, "Signal" );
causeintr( scp );
}
---------------------------------------------------------------------------
This sample routine prints 'Data: input' for normal input, 'Signal:
signal-action' when an interrupt, quit or terminal stop signal is
generated at the keyboard, 'Error: error-message' when the read()
terminates abnormally, and 'End of file' when ^D is typed (it then
exits). When the call to causeintr() is absent and an interrupt is
sent, the read does not return with an error; when it is present, the
error 'Interrupted system call' is printed.
Causeintr() is certainly simple to use -- can anyone point out any
severe drawbacks to it that would prevent it from being useful?
My personal feeling is that it is a large improvement over setjmp/
longjmp for this application... (But of course I wrote the routine!)
Donn Seeley UCSD Chemistry Dept. RRCF ucbvax!sdcsvax!sdchema!donn
32 52' 30"N 117 14' 25"W (619) 452-4016 sdcsvax!sdchema!donn at noscvax
More information about the Comp.unix.wizards
mailing list