Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using the pipe() system call

Tags:

c

posix

piping

I've been trying to use the pipe() system call to create a shell that supports piping (with an arbitrary number of commands).

Unfortunately, I haven't had much luck using pipe(). After spending a few days looking at various online resources, I decided to put together an oversimplified program that has the same effect as executing ls | sort to see if I could even get a pipe to work for two sibling, child processes. Here's the code:

#include <sys/wait.h>
#include <unistd.h>

void run(char *cmd) {
   char *args[2];
   args[0] = cmd;
   args[1] = NULL;

   execvp(cmd, args);
}

int main(void) {
    int filedes[2];
    pipe(filedes);

    pid_t pid_a, pid_b;

    if (!(pid_a = fork())) {
        dup2(filedes[1], 1);
        run("ls");
    }

    if (!(pid_b = fork())) {
        dup2(filedes[0], 0);
        run("sort");
    }

    waitpid(pid_a, NULL, 0);
    waitpid(pid_b, NULL, 0);

    return 0;
}

The pipe is created in the parent and I know that after the execvp() call, each child process inherits the file descriptors that pipe() creates in the parent. For the ls process, I'm using dup2() to redirect its standard out (1) to the write-end of the pipe and for the sort process, standard in (0) is being redirected to the read-end of the pipe.

Finally, I wait for both processes to finish before exiting.

My intuition tells me this should work, but it doesn't!

Any suggestions?

like image 543
Aryan Naraghi Avatar asked Oct 07 '11 20:10

Aryan Naraghi


People also ask

What is the use of pipe () system call?

pipe() is a system call that facilitates inter-process communication. It opens a pipe, which is an area of main memory that is treated as a "virtual file". The pipe can be used by the creating process, as well as all its child processes, for reading and writing.

How does pipe () work in C?

The pipe function creates a pipe and puts the file descriptors for the reading and writing ends of the pipe (respectively) into filedes [0] and filedes [1] . An easy way to remember that the input end comes first is that file descriptor 0 is standard input, and file descriptor 1 is standard output.

What does the pipe () system call return on success?

When a pipe() call is invoked, control traps to the kernel, creates the storage for the pipe and returns with the two pointer call descriptors of “int” type. The pointers are the returned in a array parameter to the pipe() call. It also returns a negative value in case the call failed, 0 for success.

What does pipe () return?

pipe() creates a pipe, a unidirectional data channel that can be used for interprocess communication. The array pipefd is used to return two file descriptors referring to the ends of the pipe. pipefd[0] refers to the read end of the pipe. pipefd[1] refers to the write end of the pipe.


1 Answers

You have to close the pipes you're not using. at least sort will read from its stdin until stdin is closed.

In this case, it's stdin is never closed, as you still have 2 open filedescriptors for it.

  • filedes[0] in the ls child (this likely gets closed when ls finishes)
  • filedes[0] in the parent program (this never gets closed as you waitpid() for sort to end, but it never will because the parent keeps its stdin open)

Change your program to

if (!(pid_a = fork())) {
    dup2(filedes[1], 1);
    closepipes(filedes);
    run("ls");
}

if (!(pid_b = fork())) {
    dup2(filedes[0], 0);
    closepipes(filedes);
    run("sort");
}
closepipe(filedes);
waitpid(pid_a, NULL, 0);
waitpid(pid_b, NULL, 0);

where closepipes is something like

void closepipes(int *fds)
{ 
 close(fds[0]);
 close(fds[1]);
}
like image 154
nos Avatar answered Oct 01 '22 04:10

nos