[net.unix-wizards] signal handling in 4.2: Is this

rpw3@fortune.UUCP (12/17/83)

#R:hou3c:-13500:fortune:11600030:000:3510
fortune!rpw3    Dec 17 03:25:00 1983

[Although it may sound like it, the following is not a flame, but a
serious technical challenge to the community to get their noses
up out of the alligator swamp for just a minute.]

The REAL problem is that everybody keeps trying to use UNIX signals
as if they are "software interrupts" as known by other operating
systems (like TOPS-10). UNIX signals are not "software interrupts",
they are "aborts" which you can (sometimes) catch. Just looking at
the kernel code for signals is scary! There are longjmps all the
way up from device drivers back to the system call handlers, for
crissake! How the hell is a programmer supposed to guarantee
that any kind of higher-level assertion is maintained across a
Hoare-style Monitor when 'sleep()' will longjmp on you and never
give you a chance to do a (dare I use the word "transaction"?) unwind?
(System V gives you the choice, but then you MUST do the longjmp
yourself.)

The various Berkl's have danced all around the problem, but no one has
addressed it directly. I will give you a big hint -- all this talk
about re-starting a tty read or any other read is BOGUS, a holdover
from our past. I put it to you, ladies and gentlemen, that the
following sketchy code fragment is what you really wish you could
write. It is what you CAN write on any system that truly has "software
interrupts" and "non-blocking I/O"! I would also claim that it could
be added to UNIX without breaking anything.

Let us start a discussion!

Rob Warnock

UUCP:	{sri-unix,amd70,hpda,harpo,ihnp4,allegra}!fortune!rpw3
DDD:	(415)595-8444
USPS:	Fortune Systems Corp, 101 Twin Dolphins Drive, Redwood City, CA 94065

------------------------
	#include <pi.h>

	int readhandler();	/* forward ref */
	int writehandler();	/* forward ref */

	char * devnames[NTTY] = ( "/dev/tty00",
				  "/dev/tty01",
				  ...
				  "/dev/ttynn" );

	INTVEC vecs[NVECS];	/* interrupt vectors */
	int	fd[NTTY];	/* file descr. */
	int	stat[NTTY];	/* termination status */

	main(){ int i;

		for(i = 0, i < NTTY, i++){
		    /* open the terminals */
		    fd[i] = open(devnames[i], O_RDWR | O_ASYNC);

		    /* set the interrupt vectors */
		    vecs[i].handler = readhandler;
		    vecs[i + NTTY].handler = writehandler;
		    vecs[i + 2*NTTY].handler = signalhandler;

		    /* connect the events to the vectors */
		    pi_enab(fd[i],i,IO_RD_DONE);
		    pi_enab(fd[i],i+NTTY,IO_WT_DONE);
		    pi_enab(fd[i],i + 2*NTTY,IO_ANY_SIG);

		    /* start up all the I/O */
		    stat[i] = read(fd[i], bufaddr[i], BUFSIZ);
		    stat[i+NTTY] = write(fd[i], "Banner msg\n", 11);
		    }
		pi_config(vecs,NVECS);

		notdone = TRUE;
		pi_on();	/* go! */
		while(notdone)
		    pi_wait();	/* all i/o handled in the interrupt handlers */
		exit(0);	/* alternatively, the exit could be done
				   in a handler */
	}

	readhandler(vec,fd,aux_info){
		/* here when any read completes */
		"check stat[fd]"
		   "process data"
			...
			pi_off();
			...  /* critical section */
			pi_on();
			...
		stat[i] = read(fd[i], bufaddr[i], BUFSIZ);
		pi_rte();	/* dismiss interrupt */
	}

	writehandler(vec,fd,aux_info){
		/* here when any write completes */
		"check stat[fd]"
		"process data"
		stat[i+NTTY] = write(fd[i], <next data>, 11);
		pi_rte();	/* dismiss interrupt */
	}

	signalhandler(vec,fd,aux_info){
		/* here when a device-oriented UNIX signal occurs */
		if (fd = MASTERJOB && aux_info == SIGQUIT)
			notdone = FALSE;	/* quit */
		else "other signals"
		pi_rte();
	}
----------end of code fragment--------------

henry@utzoo.UUCP (Henry Spencer) (12/21/83)

While I fully agree with Rob Warnock that signals are not software
interrupts, that they were never intended as such, and that anyone
who uses them as such is insane and deserves what he gets...  May
the gods preserve us from people who think real software interrupts
are wonderful and desirable!!!  Why do you think the first act of
practically every decent operating system in existence, when it gets
an interrupt, is to turn it into something more civilized (e.g. a
wakeup or a semaphore operation)?  Interrupts are an ugly, low-level,
terribly error-prone form of communication.  I agree that Unix could
use better interprocess communication (and that signals were never
intended to be such), but real software interrupts are the *last*
thing we want!  Higher-level primitives, **PLEASE**!
-- 
				Henry Spencer @ U of Toronto Zoology
				{allegra,ihnp4,linus,decvax}!utzoo!henry

rpw3@fortune.UUCP (01/04/84)

#R:hou3c:-13500:fortune:11600038:000:4564
fortune!rpw3    Jan  3 12:15:00 1984

<non space>
	***** fortune:net.unix-wizar / utzoo!henry /  4:20 pm  Dec 20, 1983
	While I fully agree with Rob Warnock that signals are not software
	interrupts, that they were never intended as such, and that anyone
[1] ->	who uses them as such is insane and deserves what he gets...  May
	the gods preserve us from people who think real software interrupts
	are wonderful and desirable!!!  Why do you think the first act of
[2] ->	practically every decent operating system in existence, when it gets
	an interrupt, is to turn it into something more civilized (e.g. a
	wakeup or a semaphore operation)?  Interrupts are an ugly, low-level,
	terribly error-prone form of communication.  I agree that Unix could
	use better interprocess communication (and that signals were never
	intended to be such), but real software interrupts are the *last*
[3] ->	thing we want!  Higher-level primitives, **PLEASE**!
	-- 
					Henry Spencer @ U of Toronto Zoology

The Concurrent Euclid model is indeed one of my internal refence models
when thinking about such things (c.f. Holt, R. C. "Concurrent Euclid,
The UNIX System, and Tunis", Addison-Wesley 1983). As part of a performance
analysis task, we implemented a Euclid-style "simulation kernel" which allows
multiple "processes" in one UNIX-process (c.f. Bakul Shah's articles on
"nargs()").  It works just fine doing Simula-like simulations, but when
you start hooking it to the outside world what happens? You need REAL
INTERRUPTS as the thinnest, bottom-most layer of the structure to drive
your semaphores (actually, priority condition queues).

Please understand the constraint of more-or-less "standard" UNIX.  In
terms of your note, I am TRYING to impose higher-level primitives [3],
as as fast as I can [2], and may well be insane [1] to expect to be
able to do it easily under UNIX.

Unfortunately, standard UNIXes do not allow multiple processes to
share easily and completely the same address space, as is needed for
efficiently implementing semaphore-based multiprogramming (not even
System-V, really).

Clean software interrupts ARE a useful basis for higher-level
primitives; other operating systems have provided them to users. When
complete soft-int's are provided, the code gets CLEANER, since you can
handle each interrupt as a "message" from the operating system that the
world is changed in some interesting (to you) way. (In fact, on some
systems, soft-ints are solely used to handle messages.) However, no
commercial UNIX I know of treats kernel events as messages to the user
process. (I believe there was some CMU experimental work that treated
ALL system calls and returns as messages, which would be completely
equivalent to the software interrupts I suggested.)

It has even been noted that the two most common styles of operating
systems, the procedure-call/monitor model and the message-passing
model, are in fact completely equivalent.  [Lauer & Needham, "On the
Duality of Operating System Structures", Operating System Review v13 #2
(Apr'79) pp3-19]. It was noted that attempts to do both generally
were quite messy. UNIX and Euclid both fall in the prodecure-call/monitor
camp, and I suspect efforts to graft messages on without clean interrupts
will result in further hackery.

The 4.2BSD "select" is insufficient, NOT because of the basic style
of the mechanism, but because it does not go far enough. I would be
almost satisfied with a "select" instead of "software interrupts" if
the "select" could return a "completion code" for ALL of the events the
program needs to know about: child died, <DEL> hit, timeout expired,
read complete, write complete, packet sent, IPC received, etc.
("Select" still doesn't allow the process to run CPU-bound while
handling I/O in the "foeground", though. I believe that the user process
should also have some say in how its background scheduling is handled.)

In summary, the definition of "civilized" [2] depends on where you
stand. Current UNIXes do not allow you do do a wait-for-any-interesting-
condition. It is not clear to me that the pure semaphore model (one of
several reputable models) fits well into UNIX. A clean software
interrupt system or a complete message system might be better.
My vote (currently), based on the amount of work and on upward/downward
compatibility issues, is for clean interrupts. Given that, the other
mechanisms can be built as wanted/needed.

Rob Warnock

UUCP:	{sri-unix,amd70,hpda,harpo,ihnp4,allegra}!fortune!rpw3
DDD:	(415)595-8444
USPS:	Fortune Systems Corp, 101 Twin Dolphins Drive, Redwood City, CA 94065

mason@utcsrgv.UUCP (Dave Mason) (01/05/84)

<non space>
	***** fortune:net.unix-wizar / utzoo!henry /  4:20 pm  Dec 20, 1983
	While I fully agree with Rob Warnock that signals are not software
	interrupts, that they were never intended as such, and that anyone
[1] ->	who uses them as such is insane and deserves what he gets...  May
	the gods preserve us from people who think real software interrupts
	are wonderful and desirable!!!  Why do you think the first act of
[2] ->	practically every decent operating system in existence, when it gets
	an interrupt, is to turn it into something more civilized (e.g. a
	wakeup or a semaphore operation)?  Interrupts are an ugly, low-level,
	terribly error-prone form of communication.  I agree that Unix could
	use better interprocess communication (and that signals were never
	intended to be such), but real software interrupts are the *last*
[3] ->	thing we want!  Higher-level primitives, **PLEASE**!
	-- 
					Henry Spencer @ U of Toronto Zoology

Rob Warnock replies:
The Concurrent Euclid model is indeed one of my internal refence models
when thinking about such things (c.f. Holt, R. C. "Concurrent Euclid,
The UNIX System, and Tunis", Addison-Wesley 1983). As part of a performance
....

Please understand the constraint of more-or-less "standard" UNIX.  In
terms of your note, I am TRYING to impose higher-level primitives [3],
as as fast as I can [2], and may well be insane [1] to expect to be
able to do it easily under UNIX.

Unfortunately, standard UNIXes do not allow multiple processes to
share easily and completely the same address space, as is needed for
efficiently implementing semaphore-based multiprogramming (not even
System-V, really).

Clean software interrupts ARE a useful basis for higher-level
primitives; other operating systems have provided them to users. When
....

It has even been noted that the two most common styles of operating
systems, the procedure-call/monitor model and the message-passing
model, are in fact completely equivalent.  [Lauer & Needham, "On the
Duality of Operating System Structures", Operating System Review v13 #2
(Apr'79) pp3-19]. It was noted that attempts to do both generally
were quite messy. UNIX and Euclid both fall in the prodecure-call/monitor
camp, and I suspect efforts to graft messages on without clean interrupts
will result in further hackery.
....
Rob Warnock ...!fortune!rpw3

I agree with Rob, real software interrupts are usable to build higher
level primitives (after all real hardware interrupts ARE used to build
higher level primitives), but that doesn't mean that they are the right
approach.  In particular the question that it doesn't address is providing
information about co-operating tasks to the operating system so that it may
be able to improve the scheduling.  It also means a fair amount of hacking
to build the higher level primitives.  And it doesn't address civilized I/O
by the co-op tasks.

The right solution requires looking at how the O/S is implemented..shareable
address space & synchronization primitives & private I/O.  Those are the
facilities that the O/S should be providing to the user tasks.  The problems
are in how to combine this with security for the user tasks & the O/S itself.

My approach is to make address space & process-hood independent, and to make
address spaces tree structured.   Processes being independent of the address
space means that you can use things like Concurrent Euclid to solve real
problems requiring concurrency within the confines of an O/S.  Each process
can execute independently (using multiple processors if available) doing I/O
etc. and synchronization is handled by the O/S knowing about monitors &
conditions.  The tree structured address space is used to facilitate
interactions between unrelated processes (a little detailed for this note).

I won't argue semaphores/monitors or monitors/messages, but I personally
feel monitors are the right way to go.  Hopefully others will implement
these kinds of facilities for user tasks using these alternate approaches.

-- 
 Dave Mason, U. Toronto CSRG,
	{utzoo,linus,cornell,watmath,ihnp4,allegra,floyd,decwrl,
	 decvax,uw-beaver,ubc-vision}!utcsrgv!mason