closing a pipe and duplicating a pipe

550 views Asked by At

I'm trying to understand how pipes work and it is rather confusing when I was reading this code in my textbook. In the line

dup(fd2[0]); close(fd2[0]);

why are we duplicating fd2[0],then close it right after we duplicated it?

#include <stdio.h>
#include <time.h>
#include <sys/types.h>
#include <unistd.h>

int main() {
  struct timespec ts1, ts2;
  pid_t newpid;
  int fd1[2], fd2[2];
  char m0[] = "\nabout to fork....\n";
  char m1[] = "message from parent to child\n";
  char m2[] = "message from child to parent\n";
  char m3[] = "\ndone....\n";
  char rbuf1[256];
  char rbuf2[256];
  int cn1, cn2;
  ts1.tv_sec=(time_t)1;
  ts1.tv_nsec=(long)1;
  ts2.tv_sec=(time_t)1;
  ts2.tv_nsec=(long)1;
  if ((pipe(fd1)==-1)) printf("error\n");
  if ((pipe(fd2)==-1)) printf("error\n");
  printf("fd1 %d %d fd2 %d %d\n", fd1[0], fd1[1], fd2[0], fd2[1]);

  if ((newpid=fork()) ==-1) {
    printf("failed to fork\n\n");
    return 0;
  }

  if (newpid > 0) { // parent ***************
    close(fd1[1]); close(fd2[0]); // closing 4 and 5
    dup(fd2[1]); close(fd2[1]); // taking 4 in place of 6
    write(4, m1, sizeof(m1)); // parent_to_child messg
    usleep(10000);
    cn1=read(3, rbuf1, 256);
    write(1, rbuf1, cn1);
  } else { // child ***************
    close(fd1[0]); close(fd2[1]); // closing 3 and 6
    dup(fd2[0]); close(fd2[0]); // taking 3 in place of 5
    write(4, m2, sizeof(m2)); // child_to_parent messg
    usleep(10000);
    cn2=read(3, rbuf2, 256);
    write(1, rbuf2, cn2);
  }
  write(2, m3, sizeof(m3));
  return 0;
}
2

There are 2 answers

2
Sourav Ghosh On BEST ANSWER

You did not show us your code, however, from the man page of dup()

int dup(int oldfd);

and

dup() uses the lowest-numbered unused descriptor for the new descriptor.

This clearly says, dup() will return a new file descriptor for oldfd. you need to assign the return value of dup() to get the new fd. Once you get the new fd, you can close the old fd and use the newly returned descriptor to access the file.

Once more approach, commonly used, is to close a well-known file descriptor and after that call dup() which will assign the recently closed fd as the new fd. [Example : STDIN_FILENO]

0
Some programmer dude On

The usual sequence is to have another close call before the dup call, or to assign the result of the dup and use it.

The first variant is the most common, for example to use the pipe read end as the new STDIN_FILENO you can do

close(STDIN_FILENO);  // Close the current standard input descriptor
dup(pipefds[0]);      // Duplicate the pipe read-end, which will become our new standard input
close(pipefds[0);     // Close the old pipe descriptor, to save descriptor resources

This will work because dup will select the lowest available descriptor, which after the first close call will be STDIN_FILENNO (descriptor 0).

And dup does exactly what it souds like, it duplicates the descriptor, so after a call to dup you will have two descriptors referencing the same "file", and can freely close one of them.

I suggest you read the the dup(2) manual page.