Нужна помощь в IPC через Pipes

Я работаю в лаборатории. Отцовский процесс создаст два дочерних процесса A и B. Son A отправит некоторую строку сыну B через канал. Son B инвертирует регистр String строки, полученной от Son A, и отправит обратно инвертированную строку сыну A.after получив перевернутую строку, сын А выведет ее на экран.

вот код.

#include <stdio.h>   
#include <unistd.h>   
#include <stdlib.h>
#include <ctype.h>   

void process_A(int input_pipe[], int output_pipe[])
{
    int c;    
    char ch; 
    int rc;  


    close(input_pipe[1]); 
    close(output_pipe[0]); 

    while ((c = getchar()) > 0) {
        ch = (char)c;
        rc = write(output_pipe[1], &ch, 1);
    if (rc == -1) { 
        perror("A_TO_B: write");
        close(input_pipe[0]);
        close(output_pipe[1]);
        exit(1);
        }

    rc = read(input_pipe[0], &ch, 1);
    c = (int)ch;
    if (rc <= 0) { 
        perror("A_TO_B: read");
        close(input_pipe[0]);
        close(output_pipe[1]);
        exit(1);
        }
    putchar(c);
    }
    close(input_pipe[0]);
    close(output_pipe[1]);
    exit(0);
}

void process_B(int input_pipe[], int output_pipe[])
{
    int c;   
    char ch; 
    int rc;   
    close(input_pipe[1]); 
    close(output_pipe[0]); 
    while (read(input_pipe[0], &ch, 1) > 0) {
        c = (int)ch;
        if (isascii(c) && isupper(c))
            c = tolower(c);
          else if (isascii(c) && islower(c))
            c = toupper(c);
        ch = (char)c;
        rc = write(output_pipe[1], &ch, 1);
        if (rc == -1) {
            perror("B_TO_A: write");
            close(input_pipe[0]);
            close(output_pipe[1]);
            exit(1);
        }
    }

    close(input_pipe[0]);
    close(output_pipe[1]);
    exit(0);
}


int main(int argc, char* argv[])
{
    /* 2 arrays to contain file descriptors, for two pipes. */
    int A_TO_B[2];
    int B_TO_A[2];
    int pid;       
    int rc,i,State;       

    /* first, create one pipe. */
    rc = pipe(A_TO_B);
    if (rc == -1) {
    perror("main: pipe A_TO_B");
    exit(1);
    }
    /* create another pipe. */
    rc = pipe(B_TO_A);
    if (rc == -1) {
    perror("main: pipe B_TO_A");
    exit(1);
    }

    for(i=0;i<2;i++)
    {
        if((pid=fork()) <0){perror("fork failed\n");};
        if((i==0) && (pid ==0))
        {
            process_A(A_TO_B, B_TO_A); 
        }
        else if((i==1)&&(pid==0))
        {
            process_B(B_TO_A, A_TO_B); 
        }
        else if(pid>0)
        {
           wait( &State );          
        }   
    }

    return 0;   
}

проблема в том, что когда я запускаю программу, сын Б получает блокировку. Мне нужна ваша помощь, ребята. Заранее спасибо.


person devoidfeast    schedule 19.03.2012    source источник


Ответы (1)


Хорошо, схема:

initially: parent process: has
  B_TO_A[0] and [1] open,
  has A_TO_B[0] and [1] open
fork (makes copy)
parent:                                child (pid==0):
B_TO_A both open, A_TO_B both open     call process_A: close unwanted pipe ends, loop

call wait(), wait for one child        loop reads stdin, writes one pipe, reads other pipe

if we ever get here:

fork (makes copy)
parent:                                child (pid==0):
B_TO_A both open, A_TO_B both open     call process_B: close unwanted pipe ends, loop

parent: both ends of both pipes open
call wait(), wait for one child        loop reads one pipe, writes other pipe

Во-первых, вы обычно не доберетесь до «если мы когда-нибудь доберемся сюда», потому что дочерний элемент, выполняющий process_A(), работает в цикле до тех пор, пока либо EOF на stdin (если это произойдет первым), либо один из вызовов чтения/записи канала не завершится ошибкой (например, из-за EOF на input_pipe[0]). Поскольку родитель все еще ожидает вызова wait() и оба конца обоих каналов открыты, в канале нет EOF (EOF в канале возникает после того, как вы читаете все данные, записанные всеми модулями записи, и все dups из конец записи был закрыт). Таким образом, единственный способ добраться туда — нажать EOF на стандартном вводе, чтобы цикл while не запускался.

Во-вторых, если вы снова разветвитесь и сделаете process_B(), этот дочерний элемент также будет ждать вечно, потому что один конец записи канала, из которого он читает, все еще открыт... в родительском! Родитель не закроет его, потому что родитель будет вечно ждать в wait.

В общем, что вам нужно сделать здесь:

  • создайте две трубы (как вы делаете сейчас)
  • fork один раз и запустить process_A() в дочернем
  • разветвите снова (в родительском) и запустите process_B() в (новом) дочернем
  • закрыть оба конца обеих труб (в родительском)
  • подождите обоих дочерних элементов сейчас, после того, как оба приступят к работе

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

person torek    schedule 19.03.2012
comment
Мне помогло на 100%. Спасибо торек. - person devoidfeast; 21.03.2012