In a problem given by university we have to pipe
from a parent process(P1) to its child P2, and afterwards P2 must pipe to another child of P1, the other child is P3. Both P2 and P3 are to be written in c and made into executable files. They will then by exec
ed by child processes in P1.
P1 writes the numbers 1 to 10000 to stdout
, P2 reads them through its stdin
, removes the numbers divisible by 2, and writes the result to its stdout
. P3 reads those numbers through its stdin
, filters out the results that are divisible by 3, and writes everything to a file.
I have managed to implement absolutely everything, but my child processes do not end. The reason for this, I believe, is that I have used the following method to read the input in each child:
while(n=read(0, &i, sizeof(i))>0)
The problem here, as I understand it, is that read
blocks when it doesn’t get any bytes. As P1 writes the 10000 numbers using:
for(i=1; i<=10000; i++){ write(1, &i, sizeof(i)); }
Neither child process ever has any reason to believe that no more data is coming its way. Therefore, each read
simply blocks waiting for a byte that will never come.
Can anyone suggest a way to overcome this roadblock?
The code of each process is as follows:
Parent:
#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 child1n"); 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 child2n"); 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 predictedn"); } int returnedPID2=waitpid(pid2, &status2, 0); if(returnedPID2==pid2){ printf("Parent waited for child as predictedn"); } _exit(0); }
P2 (includes excluded)
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 filen"); _exit(1); } while((n=read(0, &i, 4))>0){ if((i%3)!=0){ sprintf(tmp,"%11d", i); write(fp, tmp, strlen(tmp)); } } return; }
Thanks guys.
Advertisement
Answer
Except in case of error, exec never returns. So when you write:
execvp(args1[0],args1); if(close(fd1[0])<0){ //close used pipe error(); }
you are wrong to expect the file descriptor to be closed. Close them before you exec. They are getting left open. Although in your particular case the problem is that the parent never closes fd 1. The parent has two file descriptors that are writing into the pipe ( fd[1]
and 1
), and you need to close them both before the child reading the pipe will finish.