简体   繁体   中英

pipe chain between processes

I want to have one parent with 2 children.

The parent reads from file "a.txt" and sends trough pipe to first child; the first child reads the chars and sends to the second child the lower letter chars.

The second child prints in "b.txt" each distinct char and number of appearances(per line) and then sends trough a pipe to the parent the number of distinct chars. The parent prints the result from the second child.

I've done the pipe from parent to 1 child and to test I've put a pipe back to the parent. What I can't figure it out is how to make the pipe go to the second child. I've been searching for information on dup2 but I don't get on how to make it work.

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

void main()
{

    int pfd1[2], pfd2[2], pid1, pid2, pfin, status, fd;
    char *c = (char *)malloc(sizeof(char));

    if (pipe(pfd1) < 0) {
        printf("Eroare la crearea pipe-ului\n");
        exit(1);
    }
    if (pipe(pfd2) < 0) {
        printf("Eroare la crearea pipe-ului\n");
        exit(1);
    }
    if ((pid1 = fork()) < 0) {
        printf("Eroare la fork\n");
        exit(1);
    }

    if (pid1 == 0) {    /*child */
        close(pfd1[1]);
        while (read(pfd1[0], c, 1) > 0) {
            //printf("%s",c);
            if (islower(*c)) {
                close(pfd2[0]);
                //inchid capul de citire; scriu in pipe
                write(pfd2[1], c, 1);
                ////dup??????
            }
        }
        printf("\n");
        write(pfd[1], buff, len);

        close(pfd1[0]);
        close(pfd2[1]);
        exit(0);
    }

    if ((pid2 = fork()) < 0) {
        printf("Eroare la fork\n");
        exit(1);
    }

    if (pid2 == 0) {
        printf("second child");
        exit(0);
    }

    /* parent */
    close(pfd1[0]);
    close(pfd2[1]);
    fd = open("date.txt", O_RDONLY);
    while (read(fd, c, 1) > 0) {
        write(pfd1[1], c, 1);
    }
    close(pfd1[1]);     /* la sfarsit inchide si capatul utilizat */
    close(pfin);

    while (read(pfd2[0], c, 1) > 0)
        printf("%s", c);
    close(pfd2[0]);
    printf("%d", wait(&status));
    printf("%d", wait(&status));

}

I have a few specific comments on your code:

char *c = (char *)malloc(sizeof(char));

While there is nothing wrong with this, there is also no need for this char to be allocated from the heap. The more idiomatic approach would use char c; here and then pass &c to read(2) and write(2) system calls. (Even more idiomatic would be to use the C Standard IO facility; freopen(3) , getchar(3) , and putchar(3) -- but do not make that transition until you have this code working exactly as you want it to, because it is an additional complication with the problem you're trying to solve.)

if ((pid1 = fork()) < 0) {
    printf("Eroare la fork\n");
    exit(1);
}

By using your own error message, you are missing out on important error information. You should use perror(3) to print an error message instead. This will give you and your users an actual cause for errors that they can search for. fork(2) can fail if your user is running into the setrlimit(2) NPROC maximum process limit, the system-wide process limit, out of kernel memory.

if ((pid1 = fork()) < 0) {
   perror("Eroare la fork");
   exit(1);
}

You should also check the return value from open(2) calls. (You're supposed to also check the return values from write(2) and close(2) for errors, but handling these errors is harder. Simply printing the error and quitting is a good start for most programs.)

    while (read(pfd1[0], c, 1) > 0) {
        //printf("%s",c);
        if (islower(*c)) {
            close(pfd2[0]);

This is the wrong location for the close(2) call -- you shouldn't be closing this filedescriptor over and over for every input character. If you were checking the close(2) return value, you would notice errno is set to EBADF because the file descriptor is no longer valid on the second and subsequent calls.

Now, onto the problem you came here for: the sequence of fork() , pipe() , and dup2() calls that will hook up all your processes in a pipeline and send data back to the parent process. Since pipe(2) creates uni-directional pipe(7) s, you need to call pipe(2) four times -- for both directions between parent and children. If you store the pipe endpoints in arrays with names that mean something to you, they will be easier to keep track of. Perhaps create arrays named to_ for writing into and from_ for reading from:

int to_child[2];
int from_parent[2];
int to_parent[2];
int from_child[2];

for (int i=0; i<2; i++) {
    int p[2];

    if (pipe(p) == -1) {
    perror("pipe");
    exit(1);
    }

    /* from parent to child */

    to_child[i] = p[1];
    from_parent[i] = p[0];

    if (pipe(p) == -1) {
    perror("pipe");
    exit(1);
    }

    /* from child to parent */

    to_parent[i] = p[1];
    from_child[i] = p[0];
}

Note that you don't actually need to use dup2(2) to re-arrange the filedescriptors unless you want to execute other programs to handle the "filter" task. Just read(2) using the from_parent[...] or from_child[...] descriptors and write(2) to the to_child[...] and to_parent[...] descriptors.

Maybe the entire thing would be easier with socketpair(2) using AF_UNIX to create bi-directional sockets , which can then be read from and written to in the same fashion as any other BSD-style socket. See socket(7) and unix(7) for an overview.

You do not need dup() . Simply open the pipes in the parent and then in each process close() the endpoints you don't need and just use the endpoints you do need.

Note that this means that the parent will close both endpoints of the pipe for child-child communication. Each of the endpoints of this pipe will be used by one of the children. Each child should also close the endpoint it doesn't use.

Thank you for your answers. Here is the code for the whole problem.

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

void main()
{

 int pfd1[2],pfd2[2],pfd3[2],pid1,pid2,status,fd,fo;
 char letter[32][2];
 int letternumber=0,i,sw=0;
 char* c = (char*)malloc(sizeof(char));
 //pipe creation
 if(pipe(pfd1)<0){ 
    perror("Eroare la crearea pipe-ului"); 
    exit(1); 
 } 

 if(pipe(pfd2)<0){ 
    perror("Eroare la crearea pipe-ului\n"); 
    exit(1); 
 }

 if(pipe(pfd3)<0){ 
    perror("Eroare la crearea pipe-ului\n"); 
    exit(1); 
 }  
 //make first child
 if((pid1=fork())<0){ 
    perror("Eroare la fork\n"); 
    exit(1); 
 } 
 if(pid1==0) //child process
 { 
 if(close(pfd1[1])<0) {perror("Eroare close");exit(1);} // close write end; process will read from pipe
 if(close(pfd2[0])<0) {perror("Eroare close");exit(1);} //close read end; write in pipe
 while (read(pfd1[0], c, 1) > 0){
    if(islower(*c))  write(pfd2[1],c,1);//writing in pipe

 }
 if(close(pfd1[0])<0) {perror("Eroare close");exit(1);} /* close other ends */ 
 if(close(pfd2[1])<0) {perror("Eroare close");exit(1);}
 if(close(pfd3[0])<0) {perror("Eroare close");exit(1);}
 if(close(pfd3[1])<0) {perror("Eroare close");exit(1);}
 exit(0); 
 } 


 //make second child
 if((pid2=fork())<0){ 
  perror("Eroare la fork"); 
  exit(1); 
 } 

 if(pid2==0){ /* second child*/ 
      if(close(pfd1[0])<0) {perror("Eroare close");exit(1);}
  if(close(pfd1[1])<0) {perror("Eroare close");exit(1);}
  if((fo=open("statistica.txt", O_CREAT | O_TRUNC | O_RDWR, 
                S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | 
                S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IROTH))==-1)
    {perror("Eroare open");exit(1);}
  if(close(pfd2[1])<0) {perror("Eroare close");exit(1);}
  letter[0][0]='A';
  letter[0][1]=0;
  while(read(pfd2[0],c,1)>0) {          
    for(i=0;i<=letternumber;i++) 
          if(letter[i][0]==*c) {letter[i][1]++;sw=1;}
    if (sw==0){
          letter[letternumber][0]=*c;
          letter[letternumber++][1]=1;
    }     
    sw=0;    
  }
  printf("\n");//why won't it write to file without it; 
                   //wihtout it, it writes to screen?
  if(close(pfd2[0])<0) {perror("Eroare close");exit(1);}
  dup2(fo,1);
  for(i=0;i<letternumber;i++) 
      printf("%c %d\n",letter[i][0],letter[i][1]);
  if(close(fo)<0) {perror("Eroare close");exit(1);}
  if(close(pfd3[0])<0) {perror("Eroare close");exit(1);} //close read end; going to write in pipe
  dup2(pfd3[1],1);
  printf("%d",letternumber);
  if(close(pfd3[1])<0) {perror("Eroare close");exit(1);}
  exit(0);
 }  

 /* parent process */ 
 if(close(pfd1[0])<0) {perror("Eroare close");exit(1);} // close read end; write in pipe
 if(close(pfd2[0])<0) {perror("Eroare close");exit(1);}
 if(close(pfd2[1])<0) {perror("Eroare close");exit(1);}
 if(close(pfd3[1])<0) {perror("Eroare close");exit(1);}

 if((fd=open("date.txt",O_RDONLY))==-1)
  {perror("Eroare open");exit(1);}

 while(read(fd,c,1)>0)
      write(pfd1[1],c,1); //write in pipe

 if(close(pfd1[1])<0) {perror("Eroare close");exit(1);}
 //dup2(pfd3[0],0);
 while(read(pfd3[0],c,1)>0) printf("%c",*c);
 printf("\n");
 if(close(pfd3[0])<0) {perror("Eroare close");exit(1);}
 wait(&status);
 wait(&status);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM