Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Aren't pipes in UNIX supposed to be uni-directional?

Tags:

c

unix

pipe

Have a look at the following code:

#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<sys/types.h>

main() {
    int pipdes[2];
    char buff[50];
    const char parent[]="Parent Writes. Child Reads\n";
    const char child[]="Child Writes. Parent Reads\n";
    if(pipe(pipdes)==0) {
        pid_t pid=fork();
        if(pid<0)
              printf("Error\n");
        if(pid==0){
            read(pipdes[0],buff,50);
            printf("Parent: %s",buff);
            write(pipdes[1], child, strlen(child));
            exit(0);
        }
        else if(pid>0) {
            write(pipdes[1], parent, strlen(parent));
            wait(pid);
            read(pipdes[0], buff, 50);
            printf("Child: %s", buff);
        }
    }
    else
        printf("Error in pipe\n");
}

Now, here I have created just one pipe, but both the processes can read and write from. Aren't pipes supposed to be uni-directional. Also, when i put the conventional 'close(pipdes[0])' for parent and 'close(pipdes[1])' for child, the code doesn't work, though I add the open(pipdes[0]) function later.

My concepts with UNIX and pipes is still raw, so I might come out a bit lame here, but please do assist.

like image 718
Utumbu Avatar asked Dec 05 '11 19:12

Utumbu


People also ask

Are Unix pipes bidirectional?

No, the fact that two processes can share the same pipe doesn't make them bidirectional. If they were bidirectional, the child could read from pipe[0] and write to pipe[1], and the parent could write to pipe[0] and read from pipe[1].

Are Unix pipes unidirectional?

In Unix Like operating systems pipes work unidirectional, which means you can only use a pipe to channel output to one direction.

Are pipes unidirectional or bidirectional?

Pipes are unidirectional; one end must be designated as the reading end and the other as the writing end. Note that there is no restriction that different processes must read from and write to the pipe; rather, if one process writes to a pipe then immediately reads from it, the process will receive its own message.

Is pipe unidirectional communication?

Pipes provide a unidirectional interprocess communication channel. Like any other channels a pipe has a read end and a write end. Data written to the write end of a pipe can be read from the read end of the pipe, something like read and write offset in channels.


2 Answers

On some systems, pipes can be bidirectional. But they don't have to be, and any assumption that they will be is non-portable. In particular, they aren't on Linux.

As it is, your code has a problem -- both processes are trying to read from and write to the same pipe. The intended use for pipes is that the child writes and the parent reads, or vice versa. The current way you're doing things works for you right now, because you're reading and writing once and waiting on the child. But when you loop while trying to do things the way you're doing, you can't wait -- and without synchronization, the child will often (but not always!) end up reading what it intended to send to the parent, and vice versa.

If you want data flowing in both directions, you could use two pairs of pipes. Let's call them parent_pipe and child_pipe. The parent would read from parent_pipe[0] and write to child_pipe[1], and the child would read from child_pipe[0] and write to parent_pipe[1].

#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<sys/types.h>

int main() {
    int parent_pipe[2];
    int child_pipe[2];
    char buff[50];

    if(pipe(parent_pipe) || pipe(child_pipe)) {
        perror("pipe(...)");
        exit(1);
    }

    // As noted elsewhere, you're using `fork()` incorrectly.
    // `fork()` returns 0 to the child, and a pid to the parent, or -1 if an error
    // occurs.
    int pid = fork();
    if (pid == -1) {
        perror("fork()");
        exit(1);
    }

    if (pid == 0) {
        // this is the child process.  read from child_pipe, write to parent_pipe
        const char child[]="Child Writes. Parent Reads\n";
        int in, out;
        in = child_pipe[0];
        // in = parent_pipe[0];  // uncomment me to test with one pipe pair
        out = parent_pipe[1];

        for (int i = 0; i < 10; ++i) {
            read(in,buff,50);
            printf("Parent: %s",buff);
            // NOTE: `strlen(child)` doesn't include the nul at the end!
            write(out, child, strlen(child) + 1);
        }
    }
    else {
        // this is the parent process
        const char parent[]="Parent Writes. Child Reads\n";
        int in, out;
        in = parent_pipe[0];
        out = child_pipe[1];
        // out = parent_pipe[1];  // uncomment me to test with one pipe pair

        for (int i = 0; i < 10; ++i) {
            write(out, parent, strlen(parent) + 1);
            read(in, buff, 50);
            printf("Child: %s", buff);
        }
    }
}

Alternatively, you could use a pair of UNIX sockets created with socketpair(AF_LOCAL, SOCK_STREAM, 0, sockdes) (where sockdes is what we renamed pipdes to, since it's sockets now and not pipes). The child would read from and write to sockdes[0], and the parent would read from and write to sockdes[1]. Or vice versa.

like image 99
cHao Avatar answered Sep 27 '22 20:09

cHao


In POSIX.1-2001, pipes are unidirectional. From the man page:

pipe() creates a pair of file descriptors, pointing to a pipe inode, and places them in the array pointed to by filedes. filedes[0] is for reading, filedes[1] is for writing.

By the way, your use of fork is wrong: fork returns pid>0 for the parent and pid==0 for the child. pid<0 means there was an error.

like image 30
Klox Avatar answered Sep 27 '22 20:09

Klox