donn@sdchema.UUCP (12/14/83)
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@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@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@noscvax