execlp()无法检索正确的输入

时间:2018-03-24 11:02:49

标签: c pipe fork dup

我一直在尝试编写一个非常简单的程序,其中父进程通过管道将100行传递给子进程。然后,子进程应使用生成的行并在这些行上执行命令行程序more。 但是,当我尝试运行该程序时,它只是冻结。我小心翼翼地关闭了两个进程都没有使用的所有描述符,但我并不真正理解可能导致它的原因。

代码:

int main(void){

    int fd[2];
    if (pipe(fd) == -1){
        perror("Error creating pipe");
        return 1;
    }

    dup2(fd[1], STDOUT_FILENO);

    int i;
    for (i = 1; i <= 100; i++){
        printf("Line %d\n", i);
    }
    close(fd[1]);

    pid_t pid = fork();
    if(pid == 0) {
        dup2(fd[0], STDIN_FILENO);
        close(fd[0]);

        execlp("more", "more",(char*) NULL);
        fprintf(stderr, "Failed to execute 'more'\n");
        exit(1);
    }
    wait(NULL);
    return 0;
}

1 个答案:

答案 0 :(得分:0)

  

我小心地关闭了两个进程都没有使用的所有描述符

不是。

dup2(fd[1], STDOUT_FILENO);

在这里,您stdout fd[1]的副本。

close(fd[1]);

在此关闭fd[1],但stdout仍处于打开状态。

然后你fork。此时,两个进程都可以通过stdout访问管道的写端。

    dup2(fd[0], STDIN_FILENO);
    close(fd[0]);

在子流程中,您将fd[0]复制到stdin并关闭fd[0]

然后,当您执行more时,它仍然可以访问管道的两端(通过stdin / stdout)。

同时,您的父流程可以访问管道的两端(通过fd[0] / stdout)。

实际上你什么都没有关闭。

还有第二个问题:您的父进程写入stdout,它绑定到管道的写端,没有人读取它。根据您编写的内容,stdout是行缓冲还是块缓冲,stdout缓冲区有多大,以及管道本身可以存储多少,这本身就会死锁。如果管道已满,并且周围没有人可以从中读取,printf将会阻止。

要解决此问题,请不要在父进程中dup2,也不要在子进程启动之前写入管道。

int main(void){
    int fd[2];
    if (pipe(fd) == -1){
        perror("Error creating pipe");
        return 1;
    }

    pid_t pid = fork();
    if (pid == -1) {
        perror("Error spawning process");
        return 2;
    }

    if (pid == 0) {
        close(fd[1]);  /* close write end of the pipe in the child */
        dup2(fd[0], STDIN_FILENO);
        close(fd[0]);

        execlp("more", "more", (char*)NULL);

        fprintf(stderr, "Failed to execute 'more'\n");
        exit(1);
    }

    close(fd[0]);  /* close read end of the pipe in the parent */

    FILE *fp = fdopen(fd[1], "w");
    if (!fp) {
        perror("Error opening file handle");
        return 3;
    }

    for (int i = 1; i <= 100; i++){
        fprintf(fp, "Line %d\n", i);
    }
    fclose(fp);  /* flush and close write end of the pipe in the parent */

    wait(NULL);
    return 0;
}