Lots of programs do this, but it's often not done correctly, leading to maddening I/O errors that are hard to reproduce. This Tech Tip talks about these issues involved and presents a library function that does this remapping correctly.
... int writepipe[2], /* parent -> child */ readpipe [2]; /* child -> parent */ pid_t childpid; /*------------------------------------------------------------------------ * CREATE THE PAIR OF PIPES * * Pipes have two ends but just one direction: to get a two-way * conversation you need two pipes. It's an error if we cannot make * them both, and we define these macros for easy reference. */ if ( pipe(readpipe) < 0 || pipe(writepipe) < 0 ) { /* FATAL: cannot create pipe */ } #define CHILD_WRITE readpipe[0] #define PARENT_READ readpipe[1] #define PARENT_WRITE writepipe[0] #define CHILD_READ writepipe[1] if ( (childpid = fork()) < 0) { /* FATAL: cannot fork child */ } else if ( childpid == 0 ) /* in the child */ { close(PARENT_WRITE); close(PARENT_READ); dup2(CHILD_READ, 0); close(CHILD_READ); dup2(CHILD_WRITE, 1); close(CHILD_WRITE); /* do child stuff */ } else /* in the parent */ { close(CHILD_READ); close(CHILD_WRITE); /* do parent stuff */ }
Looking at the child portion of the code, let's imagine that this surprising file-descriptor allocation happens. What else develops in the code? Let's augment the "easy" FD macros with the imaginary file descriptors allocated:
The first dup2 attaches the CHILD_READ=3 pipe to the standard input (fd#0), but fd#0 is actually CHILD_WRITE! This has the effect of shutting down the pipe going back to the parent. And if that weren't enough, the next line actually closes fd#0, which is the child read pipe. This process has just closed both of its pipe descriptors leading to the parent - though this is likely very rare, it's also very unhelpful.#define CHILD_WRITE readpipe[0] /* fd#0 */ #define PARENT_READ readpipe[1] /* fd#1 */ #define PARENT_WRITE writepipe[0] /* fd#2 */ #define CHILD_READ writepipe[1] /* fd#3 */ dup2(CHILD_READ=3, 0); close(CHILD_READ=3); dup2(CHILD_WRITE=0, 1); close(CHILD_WRITE=0);
Can we do something about this?
When considering the read and write descriptors, there are only three classes for each one:
#define DUP2CLOSE(oldfd, newfd) ( dup2(oldfd, newfd), close(oldfd) )
read FD
(should be 0)write FD
(should be 1)Action 0 1 nothing - it's already done >=1 >1 DUP2CLOSE(rfd, 0);
DUP2CLOSE(wfd, 1);0 >1 DUP2CLOSE(wfd, 1); >1 1 DUP2CLOSE(rfd, 0); >1 0 DUP2CLOSE(wfd, 1);
DUP2CLOSE(rfd, 0);1 0 tmp = dup(wfd); close(wfd);
DUP2CLOSE(rfd, 0);
DUP2CLOSE(tmp, 1);
In addition, some older UNIX systems don't implement the dup2() system call, which makes this code substantially more complicated - but it's possible. We've actually had to do this before - it's not pretty.