tags:

views:

105

answers:

5

I'm trying to understand the usage of pipe.

A parent process will pipe and if the parent forks, the child process will inherit the pipe. So now we hae a direct link to the child process and they can communicate?

I get lost when we start to close pipes and redirect pipes. Does anyone have a good explanation on closing pipes and redirecting pipes?

Thank you in-advance.

+1  A: 

You use the dup family of system calls to change a new file descriptor to one of the first three system calls.

int new_stdout = open("filename", O_WRONLY);
/* ... error check ... */
if (!fork()) {
    /* in child */
    dup2(new_stdout, 1);
    execve("program", argv, envp);

This makes "filename" write its standard out to "filename".

nategoose
+1  A: 

The child process inherits the parent's open descriptors. Default ones are stdin, stdout, stderr. They do not provide a means for the child to communicate directly with the parent since they actually go to the console. So typically you close or redirect those so you don't pollute the parent's console I/O.

But if you have a pair of pipes open you can use their descriptors as inherited by the child to communicate both ways between the two processes. Use one descriptor per direction.

Amardeep
+2  A: 

See Pipeline.

Simple example

ls -l | less

In this example, ls is the Unix directory lister, and less is an interactive text pager with searching capabilities. The pipeline lets the user scroll up and down a directory listing that may not fit on the screen.

Creating pipelines programmatically

Pipelines can be created under program control. The Unix pipe() system call asks the operating system to construct a new anonymous pipe object. This results in two new, opened file descriptors in the process: the read-only end of the pipe, and the write-only end. The pipe ends appear to be normal, anonymous file descriptors, except that they have no ability to seek.

To avoid deadlock and exploit parallelism, the Unix process with one or more new pipes will then, generally, call fork() to create new processes. Each process will then close the end(s) of the pipe that it will not be using before producing or consuming any data.

alt text

eed3si9n
A: 

We often use a pipe. As says Amardeep, the child process inherits the parent's descriptor, including pipe.

Here is an example for 2 commands. I'm not sure of my algorithm for n commands :-)

void do__pipe(char** cmd1, char** cmd2)
{
    int fd[2]; /* fd[0] for reading fd[1] for writting */

    if (pipe(fd) == -1)
    {
       perror("pipe");
    }

    switch (fork())
    {
       case -1:
      perror("fork"); exit (1);
       case 0:
      close (fd[0]);
      dup2 (fd[1], STDOUT_FILENO);
      close (fd[1]);
      execvp (cmd1[0], cmd1);
      exit (1);
    }

    dup2(STDIN_FILENO, STDIN_FILENO);

    switch (fork())
    {
       case -1:
      perror("fork (2)"); exit (1);
       case 0:
      close (fd[1]);
      dup2 (fd[0], STDIN_FILENO);
      close (fd[0]);
      execvp (cmd2[0], cmd2);
      exit (1);
    }u

    wait((int*)NULL);
}

this code has been taken from a small shell I had to write for my studies, so char** cmd1 would be something like ["cat", "/etc/passwd"] and cmd2 could be ["wc", "-l"]

Aif
+3  A: 

Here's the deal.

  1. A pipe is a software abstraction, that has two ends, each one a fd. Whatever you write on at one end (write fd), you can read off the other end (read fd).
  2. The trick in using a pipe is that you switch the stdin of some process with the read fd of the pipe. Now, you can write to the write fd of the pipe, and when the other process reads what he thinks is his stdin, he actually reads the read fd of the pipe (and gets your data).
  3. For two way communication, you can get another pipe, and switch the stdout of the process with the write fd of the pipe. Now, whatever the process writes to its stdout, you can read off the read fd of the pipe.

Here is what it looks like:

P1=[WR FD]===========[RD FD]=[STDIN]=P2
P1=[RD FD]===========[WR FD]=[STDOUT]=P2

P1 and P2 are processes. And "===" depict the pipes.

Your question was specifically about closing and redirecting. That comes into play when you perform the switch I referred to earlier. Let's say you have obtained a pipe by using the pipe() system call.

int fd[2];
pipe(fd);

Now you create a child process, and in that process perform the switch, like so:

if (!fork()) // 0 return value indicates that you are in the child process
{
    dup2(fd[0], 0); // dup2 first closes 0 in the child process, then duplicates
                    // fd[0] as 0; keep in mind that 0 is stdin and fd[0] is the
                    // read end of the pipe

    exec(something); // start the new process, which when it reads stdin, will be
                     // reading the pipe instead
}
Ziffusion