Разве трубы в UNIX не должны быть однонаправленными? - PullRequest
7 голосов
/ 05 декабря 2011

Посмотрите на следующий код:

#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");
}

Теперь я создал только один канал, но оба процесса могут читать и записывать.Разве трубы не должны быть однонаправленными.Кроме того, когда я помещаю обычные «close (pipdes [0])» для родителя и «close (pipdes [1])» для потомка, код не работает, хотя я добавляю функцию open (pipdes [0])позже.

Мои концепции с UNIX и pipe еще не обработаны, так что я могу быть немного неубедительным, но, пожалуйста, помогите.

Ответы [ 3 ]

20 голосов
/ 05 декабря 2011

В некоторых системах трубы могут быть двунаправленными. Но они не должны быть, и любое предположение, что они будут, непереносимо. В частности, они не работают в Linux.

В действительности ваш код имеет проблему - оба процесса пытаются читать и записывать в один и тот же канал. Предполагаемое использование для каналов - то, что ребенок пишет, а родитель читает, или наоборот. Текущий способ, которым вы все делаете, работает для вас прямо сейчас, потому что вы читаете и пишете один раз и wait обращаетесь к ребенку. Но когда вы зацикливаетесь, пытаясь сделать то же самое, что и вы, вы не можете wait - и без синхронизации ребенок часто (но не всегда!) В конечном итоге читает то, что он намеревался отправить родителю. и наоборот.

Если вы хотите, чтобы данные передавались в обоих направлениях, вы можете использовать две пары каналов. Давайте назовем их parent_pipe и child_pipe. Родитель будет читать из parent_pipe[0] и писать в child_pipe[1], а ребенок будет читать из child_pipe[0] и писать в 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);
        }
    }
}

В качестве альтернативы, вы можете использовать пару сокетов UNIX, созданных с помощью socketpair(AF_LOCAL, SOCK_STREAM, 0, sockdes) (где sockdes - это то, к чему мы переименовали pipdes, поскольку теперь это сокеты, а не каналы). Ребенок читает и пишет в sockdes[0], а родитель читает и пишет в sockdes[1]. Или наоборот.

5 голосов
/ 06 декабря 2011

В POSIX.1-2001 трубы являются однонаправленными.На странице man:

pipe () создает пару файловых дескрипторов, указывающих на индекс канала, и помещает их в массив, на который указывают filedes.filedes [0] - для чтения, filedes [1] - для записи.

Кстати, вы используете fork неправильно: fork возвращает pid>0 для родителя и pid==0 для ребенка.pid<0 означает, что произошла ошибка.

4 голосов
/ 06 декабря 2011

Нет, это не так. Были некоторые системы с двунаправленными трубами (солнце, IIRC). Если вам действительно нужен двунаправленный канал, вы можете использовать socketpair ().

...