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 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