Async IO on the 4D series

Jim Barton jmb at patton.SGI.COM
Tue Feb 28 04:53:27 AEST 1989


There have been many requests for asynchronous IO facilities within the
IRIX kernel.  We haven't implemented anything yet, primarily because it
is not truly clear what a correct, easy to use interface should be.  The
Berkeley FASYNC setup on file descriptors is pretty limited, in that you
get a signal when at least one byte is available - there are no provisions
for bigger buffers, signals to multiple processes (except through process
groups) and others.

My contention has always been that IRIX has the power to perform 
asynchronous IO in user-land, without burdening the kernel and forcing
our idea of the proper semantics on people.  This is possible using 
shared processes and trivial programming techniques.

The following program is a runnable example of just such an IO scheme.  The
program sets up an asyncIO slave to read characters from the keyboard, and
then goes into a CPU intensive loop.  As characters are read, the slave
interrupts the master, passing characters to him.  Because I did this
entirely in user land, it was very simple to implement.  The output is a
series of '.' characters (for each completion of the CPU loop iteration)
interspersed with the characters read from the keyboard.  The program will
quit with a ^C (or whatever your interrupt character is).

Comments and discussion on this example, and the whole of asynchronous IO
in UNIX are welcome.

-- Jim Barton
Silicon Graphics Computer Systems    "UNIX: Live Free Or Die!"
jmb at sgi.sgi.com, sgi!jmb at decwrl.dec.com, ...{decwrl,sun}!sgi!jmb

  "I used to be disgusted, now I'm just amused."
			- Elvis Costello, 'Red Shoes'
--
------------------------- cut here ------------------------------
/*
 *	ex_async - an example program showing how asynchronous I/O
 *		   can be performed from a user-level task.
 * 	J. M. Barton 2/25/89
 *
 * This program is herewith released to the public domain.  You may do
 * as you wish with this code.  The author (J. M. Barton) and his
 * employer (Silicon Graphics, Incorporated) assume no liability of any
 * kind in connection with the use or non-use of this code or any fragment
 * of it.  Use at your own risk.  Please retain this statement with all
 * copies or fragments of code taken from this program.
 */

# include	<sys/types.h>
# include	<sys/prctl.h>
# include	<sys/schedctl.h>
# include	<termio.h>
# include	<signal.h>
# include	<stdio.h>

# define	COMSIG		SIGUSR1
# define	RTPRI		NDPHIMAX

/*
 * Example strategy is to start a slave reading from the keyboard, and it
 * will signal the parent whenever a character is read, and the parent grabs
 * the character and echo's it, and then starts the child waiting again.
 * Program will work passing any file through on it's standard input, though.
 */

struct {		/* structure we communicate through */
	int	ppid;		/* parent process ID */
	int	cpid;		/* child process ID */
	int	fd;		/* file descriptor to read */
	char	byte;		/* acquired byte */
} comarea;
struct termio	tb;	/* original TTY state */

main()
{
   int			asyncslave();
   int			asyncintr();
   int			ttyclean();
   struct termio	ta;

	/*
	 * Just for fun, start the slave first.  He will block himself.
	 */
	comarea.ppid = getpid();
	comarea.cpid = sproc(asyncslave, PR_SALL, 0);

	/*
	 * Set up the terminal to return a single byte at a time.
	 */
	if (ioctl(0, TCGETA, &tb) != -1) {
		signal(SIGINT, ttyclean);
		ta = tb;
		ta.c_line = 0;
		ta.c_iflag = BRKINT|ICRNL;
		ta.c_lflag &= ~(ICANON|ECHO);
		ta.c_cc[VMIN] = 1;
		ta.c_cc[VTIME] = 0;
		ioctl(0, TCSETA, &ta);
	}

	/*
	 * Note that we can change the file descriptor at any time, such
	 * as opening a new file, and pass that to the child.  The sproc()
	 * facility insures that the child has exactly the same file
	 * descriptors as the parent.
	 */
	comarea.fd = 0;

	/*
	 * Set up the interrupt handler and let the slave go.
	 */
	sigset(COMSIG, asyncintr);
	sigrelse(COMSIG);
	unblockproc(comarea.cpid);

	/*
	 * Now just wait for the slave to read a character and then output
	 * it.
	 */
	sieve();
	/*NOTREACHED*/
}

/*
 * On interrupt, output the character that was read.
 */
asyncintr()
{
	putchar(comarea.byte);
	unblockproc(comarea.cpid);
}

/*
 * Loop forever reading characters from the input port and passing them
 * to the master.
 */
asyncslave()
{
	/*
	 * Put us at the highest possible real-time priority so
	 * we respond to events very quickly.
	 */
	schedctl(NDPRI, 0, RTPRI);

	for (;;) {
		/*
		 * Wait for command to read and do the read.  This simple
		 * protocol insures that we never trash the communications
		 * buffer.
		 */
		blockproc(0);
		if (read(comarea.fd, &comarea.byte, sizeof(char)) != 1) {
			/*
			 * Error reading from the descriptor.  Sink
			 * the parent.
			 */
			kill(comarea.ppid, SIGINT);
			exit(1);
		}

		/*
		 * Signal the parent that we got a valid byte.
		 */
		kill(comarea.ppid, COMSIG);
	}
	/*NOTREACHED*/
}

/*
 * Function to burn up time - sieve.
 */
# define true		1
# define false		0
# define size 		(256*1024)
# define sizep1		(size+1)

sieve()
{
	register int i,prime,k,count,iter;
	char flags[sizep1];

	for (iter = 0;; iter++) {
		count=0;
		for(i = 0; i <= size;i ++)
			flags[i] = true;
		for(i = 0;i <= size;i ++) {
			if(flags[i]) {
				prime = i + i + 3;
				k = i + prime;
				while(k <= size) {
					flags[k] = false;
					k += prime;
				}
				count = count + 1;
			}
		}

		/*
		 * Gaurantee that characters read from the keyboard don't
		 * mess up our output.
		 */
		sighold(COMSIG);
		printf(".");
		fflush(stdout);
		sigrelse(COMSIG);
	}
	/*NOTREACHED*/
}

/*
 * Clean up if we were attached to a tty.
 */

ttyclean()
{
	ioctl(0, TCSETA, &tb);
	exit(1);
}



More information about the Comp.sys.sgi mailing list