简体   繁体   中英

Using pipes to connect child processes

I would like to create some kind of Chinese whispers (or telephone) game.

I create a certain number of child processes, each child process having to change 1 letter from a string.The parent changes one letter, then the first child takes the string modified by the parent and changes one more letter. The second child takes the string modified by the first one (2 letters are already changed) and changes one more and so on.

Therefore I would like to link the parent and the first child and each two consecutive children with a pipe. The problem is that this does not work for more than 3 children. The third child does not read from the second one, it reads from the parent. Could you please tell me what I am doing wrong?

    int nrch;
    nrch=(int) strtol(argv[2], (char **)NULL, 10);
    msg=argv[3];
    printf("Parent: erhalten: %s\n", msg);
    int i=0;
    msg=modify(argv[3],nrch);
    printf("Parent: weiter: %s\n", msg);
    pid_t pids[10];
    int fd[2];
    int fdold[2];

    if (pipe(fdold) == -1) {
        error("Can't create the pipe");
    }

    /* Start children. */
    for (i = 0; i < nrch; ++i) {
        if (pipe(fd) == -1) {
            error("Can't create the pipe");
        }
        if ((pids[i] = fork()) < 0) {
                error("Can't fork process");
        } 
        else if (pids[i] == 0) {

            //dup2(fd[0], 0);
            if(i==0){
                close(fdold[1]);
                close(fd[0]);
                read(fdold[0],msg,sizeof(msg));
                printf("child%d: erhalten: %s\n", (i+1), msg);
                    msg=modify(msg,i);
                printf("child%d: weiter: %s\n", (i+1), msg);
                write(fd[1],msg,sizeof(msg));   
                exit(0);
            }
            else if(i==(nrch-1)){
                close(fdold[1]);
                read(fdold[0],msg,sizeof(msg));
                printf("child%d: erhalten: %s\n", (i+1), msg);
                    msg=modify(msg,i);
                printf("child%d: weiter: %s\n", (i+1), msg);
                printf("child%d: Ende: %s\n", (i+1), msg);

                    exit(0);
            }
            else{
                close(fdold[1]);
                close(fd[0]);
                read(fdold[0],msg,sizeof(msg));

                printf("child%d: erhalten: %s\n", (i+1), msg);
                    msg=modify(msg,i);
                printf("child%d: weiter: %s\n", (i+1), msg);

                write(fd[1],msg,sizeof(msg));   
                exit(0);
            }
        }
        else{
            if(i!=0){
                close(fd[1]);
            }
            else{
                write(fdold[1], msg,sizeof(msg));

            }
            if(i>1){
                close(fdold[0]);
            }
        }
        pid_t wpid;
        int status;
        wpid = wait(&status);
        memcpy(fdold,fd,(sizeof(int)*2));
    }   
    close(fd[0]);

I think your code for the child is overly complex. You also appear to be writing from the parent in every iteration of the loop.

You correctly create a pipe for each child process, but I don't see a mechanism for passing one side of the pipe from one sibling to the next, this is something that the parent will need to manage.

In the code below I use prev_in within the parent to pass the read side of a pipe initially from the parent to the first child, and then from sibling to sibling, finally the parent uses this as the read side of its pipe.

I've not checked this code for any buffer overrun problems that might exist, I've only looked at the file descriptor management issues, you should probably take care to check for other errors.

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

char *modify (char *word, int num)
{
  printf ("Modify `%s', (%d)\n", word, num);
  word [num] = 'x';
  return word;
}

void
whisper (int id, int in, int out)
{
  char msg [100];

  read(in, msg, sizeof(msg));
  printf("child%d: heard: %s\n", id, msg);
  modify (msg, id);
  printf("child%d: says: %s\n", id, msg);
  write(out, msg, sizeof(msg));
}

void
start_whisper (int in, int out, char *msg)
{
  char buf [100];

  printf ("Parent, starting a whisper with `%s'\n", msg);
  write(out, msg, strlen (msg) + 1); /* Also send the NUL.  */

  read(in, buf, sizeof(buf));
  printf("Parent, heard back: %s\n", buf);
}

int
main (int argc, char *argv [])
{
  int nrch, i;
  char *msg;
  pid_t pids[10];

  nrch=(int) strtol(argv[2], (char **)NULL, 10);
  printf ("Number of children = %d\n", nrch);

  msg = argv[3];
  printf("Parent: original message: %s\n", msg);

  if (nrch == 0 || nrch > strlen (msg))
    error ("Invalid number of children");

  msg = modify (msg, nrch);
  printf("Parent: will start by saying: %s\n", msg);

  int fdold[2];
  int prev_in, parent_in, parent_out;

  if (pipe(fdold) == -1) {
    error("Can't create the pipe");
  }
  printf ("Parent Read: %d, Write: %d\n", fdold [0], fdold [1]);

  parent_in = fdold [0];
  parent_out = fdold [1];

  /* Start children. */
  prev_in = parent_in;
  for (i = 0; i < nrch; ++i)
    {
      int fd[2];
      if (pipe (fd) == -1) {
        error ("Can't create the pipe");
      }
      if ((pids[i] = fork()) < 0) {
        error("Can't fork process");
      }
      else if (pids[i] == 0)
        {
          /* Child.  */
          int my_in, my_out;

          close (fd [0]); /* The sibling read handle, this becomes prev_in
                             and is passed to the next sibling.  */
          close (parent_out); /* The parent write handle, only the parent
                                 wants to hold this, every child will
                                 close it.  */

          my_in = prev_in;
          my_out = fd [1];

          whisper (i, my_in, my_out);
          exit (0);
        }
      else
        {
          /* Parent.  */
          close (prev_in); /* The PREV_IN was passed the child we just
                              created, we no longer need this.  */
          prev_in = fd [0]; /* The read handle from the pipe we just made
                               will be passed to the next sibling.  */
          close (fd [1]); /* The write handle from the pipe we just made
                             was grabbed by the child we just made, we no
                             longer need this.  */
        }
    }

  start_whisper (prev_in, fdold [1], msg);

  pid_t wpid;
  int status;
  wpid = wait(&status);

  return 0;
}

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