From:	SMTP%"RELAY-INFO-VAX@CRVAX.SRI.COM" 24-MAY-1994 12:30:27.16
To:	EVERHART
CC:	
Subj:	RE: C++ cout lost after vfork()

Message-Id: <9405241511.AA09378@uu3.psi.com>
Date: Tue, 24 May 94 10:59:16 EDT
From: Jerry Leichter <leichter@lrw.com>
To: INFO-VAX@SRI.COM
Subject: RE: C++ cout lost after vfork()
X-Vms-Mail-To: UUCP%"jay@stsci.edu"

	We have a C++ application which calls vfork() to create a child.  The
	child reassigns stdin/stdout to pipes created by the parent, i.e.,

	. . .

	pid = vfork();

	if (pid == 0) {
                // New child process.  Connect pipes to stdin/stdout.

                dup2(pout[0], 0);
                dup2(pin[1], 1);		//***

		. . .
	} else {
		// Parent process

		cout << "..." << endl;		//*** FAILS!!!
	} 

	. . .

	But the parent's cout stream no longer works after this.  Certainly I
	would expect dup2() to close the stdout stream in the child process,
	but the parent's stdout/cout should not be affected like this. 

	I've tried various things to prevent this, like opening additional
	channels on the stdout stream, etc, to no avail.  Any thoughts on
	this?  Is it a bug in the DEC C RTL dup2() function? 

None of the above.  You are simply misunderstanding (a) what vfork() promises
to do for you; (b) how vfork() is actually implemented in VMS.

vfork() is *not* fork().  It was introduced in BSD Unix as a cheap way to
create new processes without actually copying old processes.  The BSD descrip-
tion of vfork() is a bit vague (so what else is new?) and says merely that the
child process "borrows the parent's memory and thread of control until a call
to execve or an exit....  The parent process is suspended while the child is
using its resources".  What "borrows" means is unspecified, but there is an
interesting hint:  "It does not work, however, to return while running in
the child context from the procedure which called vfork since the eventual
return from vfork would then return to a no longer existent stack frame.  Be
careful also to call _exit and not exit if you can't execve, since exit will
flush and close standard I/O channels and thereby mess up the parent processes
[sic] standard I/O data structures."  More simply:  The parent and child
share all their memory (though, in Unix, probably not their descriptor tables,
which are in kernel space in Unix - but the documentation doesn't really
*quite* say).

In VMS, the "descriptor table" (which in both OS's maps I/O descriptors to
actual open files) is purely a construction of the C RTL, and "lives" in
user space.  So it's shared, too.

Actually, the VMS implementation of vfork() is quite unexpected.  vfork()
doesn't actually create a new process!  It simply records some information,
very much like setjmp(), and returns 0.  It is the eventual exec() call that
creates the child process, and in effect does a longjmp() back to the the
vfork() call, returning the PID of the child.  Thus, in the VMS emulation, not
only is the *memory* shared, *everything* is "shared", since *it's the same
process*!  Hence, your comment:

	if (pid == 0) {
                // New child process.  Connect pipes to stdin/stdout.

is incorrect - more or less, in original BSD Unix, completely in VMS.  (If
you were to do a getpid() in this section of code on VMS, you'd get the pid of
the parent, not of the child - it doesn't exist yet!)

If you need to manipulate I/O descriptors before doing your exec(), you'll
have to make sure to put them back to the way they were when you are done.
You can use dup() to save them in some other descriptors, then dup2() to put
them back.

In practice, Unix shells usually do this manipulation *before* doing a fork()
and so have to put the results back, even though with fork() it appears that
they could do it more simply in the child.  When I teach operating systems,
an early assignment is usually to write a simple shell.  I ask students to
consider both techniques, and decide why Unix uses the apparently more complex
one.  Hint:  Pipes.
							-- Jerry