C中父母和两个孩子之间的管道

时间:2014-11-20 19:14:39

标签: c pipe exec fork parent-child

我正在尝试编写一个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;
}

1 个答案:

答案 0 :(得分:0)

修改与开放流关联的文件描述符是个坏消息。我认为它很可能会给你带来麻烦,而且这里也没有必要这样做。父母应该使用fdopen()在管道末端打开新流,并通过它们而不是通过标准流与子代进行I / O.除了更安全之外,还使流程的原始标准流可用于与其父流程进行通信。

使用这种方法,您甚至可以流式传输字符串以在进程之间来回排序,而不是在每个进程的内存中冗余地缓冲它们的块。例如,您可能会这样做:

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