tags:

views:

179

answers:

3

I have a problem with this exercise:

Write a program in C that creates a child and between father and child, there will be two-way communication using pipes. His father would read a file (whose name will be given the user) and will send letters to the child.

The child will count the number of words starting from with 'a'; if the number is, whether X of words, X, starting with 'a' is greater than 5, then the child will create his own child (grandchild). The grandchild of the establishment will send, by whatever means you deem possible, the grandfather * value of X number and exit. [By inference: if the number X is less than 6, then the child simply exits, and the 'would be grandparent' also exits.]

  • Note: Grandfather = initial process of the father, that father of his father's grandchild

Here is what I have done till now; please help me...

#include <stdio.h>
#include<string.h>
#include <stdlib.h>
#include <fcntl.h>
int main(int argc, char *argv[]) 
{ 
 int fd1[2], fd2[2], pid, status,sum=0, i;
 char gram[100], x;
 char buff[100];
 char rev[1000];
FILE *fp;
 if (pipe(fd1) == -1) { /* Create a pipe */
  perror("pipe");
  exit(1);
 }
 pid = fork();
 switch (pid)
  { 
 case -1:
  perror ("Fork error\n");
  exit(99); // in case of error
 case 0:
  close(fd1[1]);//Close the writing side 

  rev[1000]= read(fd1[0], buff, 1000); /* Read from the pipe */
  /*for (i=0; i< 1000 ; i++)
  {
   rev[i] = read(fd1[0], buff, sizeof(buff));
  }*/

   while( rev[i] != '\0')
   { 
    if (buff[i] == 'a' )
    {  sum++;
      i++;
    }  
    if (rev[i] == "")
    {

     if (rev[i+1]) //elenxei to epomeno tou kenou
     {
      sum++;
      i++;
     }
    }
    i++;
   }
   printf("%d", sum);

  exit(0);
 default:
  printf("dwse arxeio\n");
  scanf("%s", gram);
  close(fd1[0]);//Close the reading side
  fp = fopen (gram,"r"); 
  getc(fp);
  fclose(fp);
  write(fd1[1], buff, sizeof(buff)+1);
  close(fd1[1]);//Close the writing side
  wait(&status); // waits till the child process  ends
 } 
}
+1  A: 

You probably want to look at popen. It starts a child process and returns a FILE * you can use to read/write to the childs stdin/stdout.

JimR
The question is about bidirectional communication, `popen` only provides unidirectional communication.
Peter G.
+1  A: 

Let's call the three processes GP (for grandparent), PP (for parent process), and GC (for grandchild). In outline, I think what you need to do is:

  1. GP creates two pipes (4 file descriptors) designated RP (read pipe) and WP (write pipe). That decribes how GP will use them; PP and GC will write on RP and read on WP.
  2. GP forks, creating PP.
  3. GP will close the write end of RP and the read end of WP.
  4. GP will open the file.
  5. GP will write whatever subset of the file is appropriate to PP via WP.
  6. GP will close WP when there is no more data to transfer.
  7. GP should also close the file it opened.
  8. GP will then read from RP, probably stashing the data until it gets EOF.
  9. If it gets any information back, GP will echo that information to standard output.
  10. GP can then terminate.

Meanwhile, step 2 above created PP, who has to do some work:

  1. PP needs to close the read end of RP and the write end of WP.
  2. PP sits in a loop, reading data from WP, counting whatever is relevant.
  3. When PP gets an EOF on WP, it can decide what it needs to do.
  4. PP can now close the read end of WP.
  5. If its counter X is bigger than 5, then (for reasons that only make sense in homework) it will fork to create GC; it can then exit.
  6. If its counter X does not reach the threshold, then as far as the specification goes, it can terminate immediately. For debugging, you'll probably have it print something to stdout about what it did and why.

Now you have GP and GC around; remember that GC is an almost exact copy of PP, and (in particular) GC knows the value X just as well as PP did. So, GC does some work, too:

  1. GC formats X as a string (probably - you could do binary data transfer if you prefer, but that passes the formatting buck to GP, that's all).
  2. GC writes the formatted X on the write end of RP.
  3. GC closes the write end of RP.
  4. GC exits.

GC's step 3 ensures that GP wakes up from its step 8.

Judged as an industrial design, there is no point to creating GC; PP could perfectly well do what GC does. As a homework exercise, it passes muster. The key insight is in the comments above:

The crux of the matter is how to communicate from grandchild to grandfather. The good news is, the grandchild inherits the pipes open in the child.

The other key steps are closing the unused ends of the pipes and closing the pipes when there's nothing more to write. Without those closes, processes are apt to get stuck in deadlock. If GP fails to close the write end of RP, for example, it will never get EOF when reading from RP, because there is a process that could still write to RP - and that process is GP!

Jonathan Leffler
A: 
rev[1000]= read(fd1[0], buff, 1000); /* Read from the pipe */

What are you trying to accomplish with the lvalue here?

First, rev is declared as having 1000 elements, so rev[1000] is a buffer overrun...

Second, I suggest you look at the "Return value" section of the manual page for read(). It returns the number of bytes received (which may be smaller than the third parameter you specified), or 0 on end-of-file, or negative on failure. It will fill in the contents of buff with the actual data. I am not sure from your code what you are expecting the system call to act like but it doesn't seem to me like you are using it correctly.

You want to be doing something like this:

int r;

r = read(fd1[0], buff, sizeof(buff));
if (r < 0)       { /* TODO: Handle error */ }
else if (r == 0) { /* TODO: Handle EOF */ }
else             { /* TODO: Handle the fact that buff now contains 'r' bytes */ }
asveikau