[comp.unix.wizards] Combined fork/exec

KLH@SRI-NIC.arpa (Ken Harrenstien) (09/22/87)

The discussion on combining the functions of fork() and exec() is
rather interesting, as I recently implemented a forkexec() call for
the TOPS-20 C implementation that we distribute (KCC).  Perhaps
the following description will provide additional food for thought.

As background, I should explain that at SRI-NIC we create (and
sometimes import) a lot of software which we try to make portable
between TOPS-20 and 4.3BSD UNIX.  Not all of these programs use (or
can use) just the portable C functions, so we have implemented a large
number of the 4.3 system calls, including fork, vfork, and exec.

The problem with fork(), or even vfork(), is the usual one.  If the
intention of v/fork() really is to create another instance of the same
program, then the time needed to copy the entire process image (even
mapping it with vfork takes time) is justifiable.  But in most cases,
the fork() is followed almost immediately by an exec(), and the time
consumed is just so much useless overhead.  I believe vfork() was
primarily created as a means of reducing this overhead, but it seems
more like a failure of nerve -- it doesn't go far enough to help either
process creation or process mapping.

On TOPS-20, the most efficient way of forking off a new program is to
first create an (empty) inferior fork, and then map the program file
into that subfork.  Other systems have similar mechanisms which likewise
disagree with the fork/exec model.  While I think that each system
should provide as fine-grained access as possible to such functions
(the pt_xxx calls in a recent message are an example), it's unlikely
that any particular system would be able to easily support all of
these specific things.  Accordingly, for maximum portability we have
to combine existing functions rather than fragment them; by using a
different new function for each desired overall combination of
operations, we can leave it up to the implementation to perform the
combined function most efficiently.

Hence, I created forkexec() to combine fork() and exec() and whatever
related operations seem necessary.  To avoid namespace problems, there
is just this single new call; to specify a variety of desired
operations, flags are used; to ensure future upwards compatibility,
the only argument is a structure pointer.  Because all of its generic
operations can be performed with UNIX primitives, it is portable to
UNIX without requiring kernel changes.  And meanwhile, on systems like
TOPS-20, programs which previously suffered with fork() now enjoy vastly
improved performance.
		#################################
Synopsis:
	#include <frkxec.h>

	int forkexec(fxp)	/* Returns 0 on success, -1 if failure */
	    struct frkxec *fxp;

Partial contents of <frkxec.h>:

struct frkxec {
	int fx_flags;		/* Flag args to forkexec() */
	char *fx_name;		/* Program name */
	char **fx_argv;		/* Argument vector (if any) */
	char **fx_envp;		/* Environment vector (if any) */
	int fx_pid;		/* PID of created subfork (if won) */
	int fx_waitres;		/* wait() result if FX_WAIT was set */
	int fx_fdin;		/* FX_FDMAP: New std input fd unless -1 */
	int fx_fdout;		/* FX_FDMAP: New std output fd unless -1 */
	...			/* Other stuff depending on flag options */
};
	/* forkexec() flags */
#define FX_NOFORK	01	/* Do chain (exec), not subfork */
#define FX_PGMSRCH	02	/* Do shell-search for program name */
#define FX_FDMAP	04	/* Map standard I/O FDs from fdin, fdout */
#define FX_WAIT		010	/* Wait for subfork to finish */
	...	/* Other flag options for PC setting, etc. */
	...	/* FX_T20_xxx flags exist for T20-specific capabilities. */
		#################################

	These operations are not meant to represent everything that
forkexec() could be used for; they are just the ones that I found most
immediately useful, and obviously many others could be added.
	Note that if UNIX primitives were being used, most of the
structure specifies actions that should take place in the child after
a fork() is done, such as FX_FDMAP to redefine the standard input and
output for the new process.  The only exception is FX_WAIT, which is
very convenient and happens to be much easier to do on TOPS-20 while
still in the context of forkexec().  But I didn't want to call it
forkexecwait()...
-------

jh@pcsbst.UUCP (10/05/87)

We did a forkexecve() implementation here at PCS a long time
ago. We used the execve() parameters and succeded in combining
the semantics of fork() and execve().

This was a not big job. The complicated thing was to modify the
sh to use this system call instead of fork() and execve(),
because it sometimes does a lot of things inbetween the fork()
and the execve() which actually may be done before the fork.
A proper use of the "close-on-exec" flag helps. Sometimes, however,
the fork() can (and should) not be eliminated because the shell
executes a shell script and does no execve().

		Johannes Heuft

		unido!pcsbst!jh