2014-11-20 3 views
2

Я пытаюсь написать программу на C, которая использует каналы для отправки информации между родителем и двумя детьми. Целью программы является достижение чего-то похожего на сортировку слияния для строк. Я прочитал количество строк, а затем строк. Строки делятся между двумя детьми, рекурсивно, пока каждый ребенок не имеет только одну строку. Я должен перенаправить stdin ребенка, чтобы читать его из stdout родителя.Трубы между родителем и двумя детьми в C

По какой-то причине ни один из детей не читает больше, чем первая строка. Как я могу решить эту проблему?

int main(int argc, char * argv[]) { 
    int nrrows = 0; 
    char * buffer = NULL; 
    size_t n = 0; 
    getline(&buffer, &n, stdin); 
    char * endptr; 
    nrrows = strtol(buffer, &endptr, 10); 

    char rows[nrrows][MAX_LEN]; 
    int i = 0; 
    n = 0; 
    while(i < nrrows) { 
     char * row = NULL; 
     getline(&row, &n, stdin); 
     strcpy(rows[i], row); 
     i++; 
    } 

    if(nrrows == 1) { 
     fprintf(stderr, "%s", rows[0]); 
     return 0; 
    } 

    int fdcp1[2]; 
    int fdcp2[2]; 
    if(pipe(fdcp1) < 0) { 
     fprintf(stderr, "pipe unsuccessfull\n"); 
     return EXIT_FAILURE; 
    } 
    if(pipe(fdcp2) < 0) { 
     fprintf(stderr, "pipe unsuccessfull\n"); 
     return EXIT_FAILURE; 
    } 

    pid_t chpid1 = fork(); 
    if(chpid1 < 0) { 
     fprintf(stderr, "fork unsuccessfull\n"); 
     return EXIT_FAILURE; 
    } 
    else if(chpid1 == 0) { 
     close(fdcp2[0]); 
     close(fdcp2[1]); 

     close(fdcp1[1]); 
     dup2(fdcp1[0], STDIN_FILENO); 
     execlp("./forksort", "child1", NULL); 
    }else { 
     close(fdcp1[0]); 
     dup2(fdcp1[1], STDOUT_FILENO); 

     double half = (nrrows/2); 
     int h = half; 
     char b[2]; 
     b[0] = '0' + h; 
     b[1] = '\n'; 
     write(fdcp1[1], b, sizeof(b)); 

     for(i = 0; i < h; i ++) { 
      rows[i][strlen(rows[i])] = '\0'; 
      write(fdcp1[1], rows[i], sizeof(rows[i])); 
     } 

     pid_t chpid2 = fork(); 
     if(chpid2 < 0) { 
      fprintf(stderr, "fork unsuccessfull\n"); 
      return EXIT_FAILURE; 
     }else if(chpid2 == 0) { 
      close(fdcp1[0]); 
      close(fdcp1[1]); 

      close(fdcp2[1]); 
      dup2(fdcp2[0], STDIN_FILENO); 
      execlp("./forksort", "child2", NULL); 
     }else { 
      close(fdcp2[0]); 
      dup2(fdcp2[1], STDOUT_FILENO); 
      half = (nrrows/2); 
      h = half; 
      char b[2]; 
      b[0] = '0' + (nrrows - h); 
      b[1] = '\n'; 
      write(fdcp2[1], b, sizeof(b)); 

      for(i = h; i < nrrows; i ++) { 
       rows[i][strlen(rows[i])] = '\0'; 
       write(fdcp2[1], rows[i], sizeof(rows[i])); 
      } 
     } 
    } 
    return 0; 
} 
+0

Не были бы эти строки 'pid_t chpid2 = fork(); pid_t chpid1 = fork(); 'вызывает создание дополнительного дочернего элемента первого ребенка? chpid2 и chpid1 будут созданы родителем, но другой chpid1 будет создан chpid2 родителя. –

+0

Хорошо, я это знаю. Но они еще не умеют читать. – Syn

+0

Извините, у меня нет времени и будет погружаться в код, но, возможно, труба просто закрыта перед другим ребенком. Просто исправьте код forking и проверьте, не исправляет ли он поведение. –

ответ

0

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

При таком подходе вы могли бы даже передавать строки, которые нужно сортировать взад и вперед среди процессов, вместо избыточной буферизации блоков из них в памяти каждого процесса. Например, вы можете сделать что-то вроде этого:

#define _GNU_SOURCE 
#include <stdio.h> 
#include <string.h> 
#include <stdlib.h> 
#include <unistd.h> 

int main(int argc, char * argv[]) { 
    char * buffer = NULL; 
    size_t buflen = 0; 
    int nrrows; 
    int fdpc1[2]; 
    int fdcp1[2]; 
    int fdpc2[2]; 
    int fdcp2[2]; 
    pid_t chpid1; 
    pid_t chpid2; 
    FILE *pipeout; 
    FILE *pipein1; 
    FILE *pipein2; 
    int half; 
    int i; 

    fprintf(stderr, "%s!!!!!!!!!!!!!!!!!\n", argv[0]); 

    getline(&buffer, &buflen, stdin); 
    fprintf(stderr, "number: %s from %s\n", buffer, argv[0]); 
    nrrows = strtol(buffer, NULL, 10); 

    if(nrrows <= 0) { 
     fprintf(stderr, "This is not a valid >0 number\n"); 
     return EXIT_FAILURE; 
    } else if (nrrows == 1) { 
     /* ... read and echo back the one row ... */ 
     getline(&buffer, &buflen, stdin); 
     fprintf(stderr, "%s", buffer); 
     return EXIT_SUCCESS; 
    } 

    /* There are at least two rows to sort */ 

    if (pipe(fdcp1) < 0) { 
     fprintf(stderr, "pipe unsuccessfull\n"); 
     return EXIT_FAILURE; 
    } 
    if (pipe(fdpc1) < 0) { 
     fprintf(stderr, "pipe unsuccessfull\n"); 
     return EXIT_FAILURE; 
    } 

    chpid1 = fork(); 
    if (chpid1 == 0) { 
     /* this is child process 1 */ 
     close(fdcp1[1]); 
     close(fdpc1[0]); 
     dup2(fdcp1[0], STDIN_FILENO); 
     close(fdcp1[0]); 
     dup2(fdpc1[1], STDOUT_FILENO); 
     close(fdpc1[1]); 
     execlp("./forksort", "child1", NULL); 
    } else if (chpid1 < 0) { 
     fprintf(stderr, "fork unsuccessfull\n"); 
     return EXIT_FAILURE; 
    } 

    /* this is the parent process */ 

    close(fdcp1[0]); 
    close(fdpc1[1]); 

    if (pipe(fdcp2) < 0) { 
     fprintf(stderr, "pipe unsuccessfull\n"); 
     return EXIT_FAILURE; 
    } 
    if (pipe(fdpc2) < 0) { 
     fprintf(stderr, "pipe unsuccessfull\n"); 
     return EXIT_FAILURE; 
    } 

    chpid2 = fork(); 
    if (chpid2 == 0) { 
     /* this is child process 2 */ 
     close(fdcp1[1]); 
     close(fdpc1[0]); 
     close(fdcp2[1]); 
     close(fdpc2[0]); 
     dup2(fdcp2[0], STDIN_FILENO); 
     close(fdcp2[0]); 
     dup2(fdpc2[1], STDOUT_FILENO); 
     close(fdpc2[1]); 
     execlp("./forksort", "child2", NULL); 
    } else if (chpid2 < 0) { 
     fprintf(stderr, "fork unsuccessfull\n"); 
     return EXIT_FAILURE; 
    } 

    /* this is the parent process */ 

    close(fdcp2[0]); 
    close(fdpc2[1]); 

    /* copy the first half of the lines from input to child 1 */ 

    pipeout = fdopen(fdcp1[1], "w"); 
    if (pipeout == NULL) { 
     fprintf(stderr, "fdopen unsuccessful\n"); 
     return EXIT_FAILURE; 
    } 

    half = nrrows/2; 
    fprintf(pipeout, "%d\n", half); 
    for (i = 0; i < half; i += 1) { 
     getline(&buffer, &buflen, stdin); 
     fprintf(stderr,"row[%d] from %s: %s", i, argv[0], buffer); 
     fputs(buffer, pipeout); 
    } 
    fclose(pipeout); 

    /* copy the second half of the lines from input to child 2 */ 

    pipeout = fdopen(fdcp2[1], "w"); 
    if (pipeout == NULL) { 
     fprintf(stderr, "fdopen unsuccessful\n"); 
     return EXIT_FAILURE; 
    } 

    fprintf(pipeout, "%d\n", nrrows - half); 
    for (; i < nrrows; i += 1) { 
     getline(&buffer, &buflen, stdin); 
     fprintf(stderr,"row[%d] from %s: %s", i, argv[0], buffer); 
     fputs(buffer, pipeout); 
    } 
    fclose(pipeout); 

    /* now read and merge sorted lines from the children */ 

    pipein1 = fdopen(fdpc1[0], "r"); 
    pipein2 = fdopen(fdpc2[0], "r"); 
    if (pipein1 == NULL || pipein2 == NULL) { 
     fprintf(stderr, "fdopen unsuccessful\n"); 
     return EXIT_FAILURE; 
    } 

    /* ... */ 

    fclose(pipein1); 
    fclose(pipein2); 

    return 0; 
} 
+0

И в моем ограниченном тестировании, который, как представляется, демонстрирует успешную связь всех дочерних процессов. –

+0

Я обновил свой код и подробно описал свои требования. – Syn

+0

Если неясно: каждый родитель открывает новые потоки поверх своих концов труб, чтобы поговорить со своими детьми; с другой стороны, детские концы этих труб сопоставляются с их стандартными потоками. –

Смежные вопросы