当read()阻止

时间:2017-12-19 20:14:30

标签: c linux pipe

在大学提出的问题中,我们必须pipe从父进程(P1)到其子P2,然后P2必须管道到P1的另一个孩子,另一个孩子是P3。 P2和P3都用c编写并制成可执行文件。然后,他们将通过P1中的子进程exec进行编辑。

P1将数字1到10000写入stdout,P2通过其stdin读取它们,删除可被2整除的数字,并将结果写入其stdout。 P3通过stdin读取这些数字,过滤掉可被3整除的结果,并将所有内容写入文件。

我已经成功实现了一切,但我的孩子流程并没有结束。我相信,其原因是我使用以下方法来读取每个孩子的输入:

while(n=read(0, &i, sizeof(i))>0)

根据我的理解,这里的问题是read在没有任何字节时阻塞。当P1使用:

写入10000个数字时
for(i=1; i<=10000; i++){
        write(1, &i, sizeof(i));
    }

任何一个儿童进程都没有任何理由相信没有更多的数据可以实现。因此,每个read只会阻塞等待永远不会出现的字节。

有人能提出一种方法来克服这个障碍吗?

每个流程的代码如下:

父:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<string.h>
#include<sys/wait.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>


int main()
{
        // pipe to send input string from parent
        // Child prints to file 
        int fd1[2];  // Used to store two ends of first pipe
        int fd2[2];  // Used to store two ends of second pipe

    //variables
    int n, i, status1, status2;
    char *args1[]={"./Div2",NULL};
    char *args2[]={"./Div3",NULL};


    //child process ids
        pid_t pid1, pid2;

    //open pipe 1
        if (pipe(fd1)==-1)
        {
            fprintf(stderr, "Pipe 1 Failed" );
            return 1;
        }
    //open pipe 2
    if (pipe(fd2)==-1)
        {
            fprintf(stderr, "Pipe 2 Failed" );
            return 1;
        }

    //create child 1    
    pid1 = fork();  
    if(pid1<0){
        printf("Error creating child1\n");
        return(1);
    }
    if(pid1==0){ //child1
        if(close(fd1[1])<0){ //child does not write to pipe 1
            error();
        }
        if(close(fd2[0])<0){ //child does not read from pipe 2
            error();
        }
        dup2(fd1[0], 0); //redirect stdin
        dup2(fd2[1], 1); //redirect stdout
            execvp(args1[0],args1);
        if(close(fd1[0])<0){ //close used pipe
            error();
        }
        if(close(fd2[0])<0){ //close used pipe
            error();
        }
        exit(0);
    }

    pid2=fork();
    if(pid2<0){
        printf("Error creating child2\n");
        return(1);
    }
    if(pid2==0){ //child2
        if(close(fd1[0])<0){ //child does not use pipe 1
            error();
        }
        if(close(fd1[1])<0){ 
            error();
        }
        if(close(fd2[1])<0){ //child does not write to pipe 2
            error();
        }
        dup2(fd2[0], 0); //redirect stdin
        execvp(args2[0], args2);
        if(close(fd2[0])<0){ //close pipe after use
            error();
        }
        exit(0);
    }


    //parent
    //parent doesn't read from the pipe
    if(close(fd1[0])<0){
    error();
    }
    if(close(fd2[0])<0){
    error();
    }
    if(close(fd2[1])<0){
    error();
    }
    dup2(fd1[1], 1); //redirect stdout
    for(i=1; i<=10000; i++){
        write(1, &i, sizeof(i));
    }
    if(close(fd1[1])<0){
    error();
    }
    int returnedPID1=waitpid(pid1, &status1, 0);
    if(returnedPID1==pid1){
    printf("Parent waited for child as predicted\n");
    }
    int returnedPID2=waitpid(pid2, &status2, 0);
    if(returnedPID2==pid2){
    printf("Parent waited for child as predicted\n");
    }
    _exit(0);

}

P2(包括已排除)

int main()
{
    int n;
    int i;
    while((n=read(0, &i, 4))>0){
        if((i%2)!=0){
            write(1, &i, sizeof(i));
        }   
    }
    return;
}

P3

int main()
{
    int n;
    int i;
    char tmp[12] = {0x0};
    char *arg[]= {"/home/eric/Documents/pr3/test.txt"};
    int fp = open(arg[0], O_WRONLY|O_CREAT|O_TRUNC, 0666);
        if(fp<0){
            printf("Error opening file\n");
            _exit(1);
        }

    while((n=read(0, &i, 4))>0){
        if((i%3)!=0){
            sprintf(tmp,"%11d", i);
            write(fp, tmp, strlen(tmp));
        }   
    }
    return;
}

谢谢你们。

1 个答案:

答案 0 :(得分:5)

除非出现错误,否则exec永远不会返回。所以当你写:

    execvp(args1[0],args1);
    if(close(fd1[0])<0){ //close used pipe
        error();
    }

您希望关闭文​​件描述符是错误的。你执行前关闭它们。他们正在开放。虽然在您的特定情况下问题是父节点永远不会关闭fd 1.父节点有两个写入管道的文件描述符(fd[1]1),您需要在它们之前关闭它们阅读管道的孩子将完成。