tags:

views:

1108

answers:

2

I'm not sure if I am even barking up the right tree here... but here goes.

I'm trying to pass data from my parent process to all children. It's a simple server program that basically will keep a list of connected clients and then send the routing table of connected clients to every client. This is eventually going to include a struct of information about each client... but for right now I just want to get every forked process to get the same information from the parent.

In the parent process, first I set up my pipes and set them to nonblocking (for when there isn't any new data available in the pipe). After a connection is made with a client the number of entries variable is increased to reflect this new connection. I then fork a child process to a new function and update my array of pipes with the new number of table entries (I have 10 pipes at the moment to see if I needed to keep a separate pipe for each child).

            pid_t pid;
 int numchildren;

 int i, n;

 /* Create the pipes. */
 for(i = 0; i < 10; i++)
 {
  if (pipe (mypipe[i]))
  {
  fprintf (stderr, "Pipe failed.\n");
  return EXIT_FAILURE;
  }
 }

 for(i = 0; i < 10; i++)
 {
  for(n=0; n<2; n++)
  {
    // Get previous flags
    int f = fcntl(mypipe[i][n], F_GETFL, 0);
    // Set bit for non-blocking flag
    f |= O_NONBLOCK;
    // Change flags on fd
    fcntl(mypipe[i][n], F_SETFL, f);
  }
  //close(mypipe[i][0]);
 }

     pid = fork();

 if (pid == (pid_t) 0)
 {
  close (mypipe[numentries-1][1]);
  recievecmds(new_fd, mypipe[numentries-1][0]);
  close(new_fd);
  return EXIT_SUCCESS;
 }

else if (pid < (pid_t) 0)
{
 fprintf (stderr, "Fork failed.\n");
 return EXIT_FAILURE;
}
else
{
 sprintf (buf,"%d",numentries);
 for(i = 0; i < 10; i++)
  write(mypipe[i][1], buf, strlen(buf));
 memset(&buf, 0, sizeof buf);
}

And then I try to read whats in the pipe in the recievecmds() function:

nbytes = read(mypipe[childindex][0], buf, sizeof(buf));

The first client connected tells me numentries = 1, the second client tells me numentries = 2 and so on. I mean I really don't even see the point for a pipe since it seems that whatever I put in the pipe I could just pass it in the function I called on the fork. Am I going about this the wrong way? It's been very frustrating trying to figure this out. How can I keep all of my child processes updated concurrently from my parent process?

Thank you so much in advance.

edit - My main problem was that I was redeclaring the pipe everytime in an infinite while loop. Very dumb mistake, immediately realized that was probably the root of my problem. However, while now the first child/pipe combo contains the correct data... the second does not. I'll see if I can figure this out on my own, thanks for the advice!

Of course now I'm running into problems because I manually select an option to get the data off the pipe. I'm going to have to think up a way to maybe either get the data for all pipes every time it's updated or make sure to get just the newest data (probably just one char at a time).

Thanks for putting up with me guys! And I apologize about not posting the whole program... but there's quite a bit. I definitely should have mentioned that I have it in an infinite loop.

+6  A: 

Various observations

  1. Don't make the pipes non-blocking; you want the children to block when there's no data. At least, in the early phases of the design; later, you may want to let them get on with work when there's no data waiting.
  2. You need to be careful with your plumbing. The parent needs 10 pipes, one for each child. But it only needs the write end of the pipe, not the read end.
  3. The children each need one pipe, for reading. Any superfluous pipes (for example, the write ends of the pipes that the parent had already opened before forking the Nth child) need to be closed.
  4. You could consider using threads - in which case you could perhaps pass the data to the children. But in the long term, it appears that you will be periodically passing data to the children, and then you need a mechanism to get the data to them (other than the function call).
  5. Pipes are 'easy' as long as you pay meticulous attention to which file descriptors are in use. Close all the descriptors you do not need.
  6. The parent will have to loop around all ten pipes writing the same data to each.
  7. It will also need to consider what to do if a child exits. It should close the pipe (no use any more), and decide whether to start a new child (but how will it ensure that the new child has all the accumulated information it needs?).
  8. Watch out for SIGPIPE - maybe install a handler, or maybe use SIG_IGN and detect write errors instead of signals.


Working code

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

enum { NUM_CHILDREN = 10 };
enum { NUM_MESSAGES = 10 };

static int write_pipes[NUM_CHILDREN];
static int n_pipes;

static void be_childish(int *pipe)
{
    int i;
    char buffer[32];
    int nbytes;
    int pid = getpid();
    close(pipe[1]);
    for (i = 0; i < n_pipes; i++)
        close(write_pipes[i]);
    printf("Child %d\n", pid);
    while ((nbytes = read(pipe[0], buffer, sizeof(buffer))) > 0)
    {
        printf("Child %d: %d %.*s\n", pid, nbytes, nbytes, buffer);
        fflush(0);
    }
    printf("Child %d: finished\n", pid);
    exit(0);
}

int main(void)
{
    pid_t pid;
    int i, j;

    /* Create the pipes and the children. */
    for (i = 0; i < NUM_CHILDREN; i++)
    {
        int new_pipe[2];
        if (pipe(new_pipe))
        {
            int errnum = errno;
            fprintf(stderr, "Pipe failed (%d: %s)\n", errnum, strerror(errnum));
            return EXIT_FAILURE;
        }
        if ((pid = fork()) < 0)
        {
            int errnum = errno;
            fprintf(stderr, "Fork failed (%d: %s)\n", errnum, strerror(errnum));
            return EXIT_FAILURE;
        }
        else if (pid == 0)
        {
            be_childish(new_pipe);
        }
        else
        {
            close(new_pipe[0]);
            write_pipes[n_pipes++] = new_pipe[1];
        }
    }

    for (i = 0; i < NUM_MESSAGES; i++)
    {
        char message[30];
        int len;
        snprintf(message, sizeof(message), "Message %d", i);
        len = strlen(message);
        for (j = 0; j < n_pipes; j++)
        {
            if (write(write_pipes[j], message, len) != len)
            {
                /* Inferior error handling; first failure causes termination */
                fprintf(stderr, "Write failed (child %d)\n", j);
                exit(1);
            }
        }
        sleep(1);
    }
    printf("Parent complete\n");

    return 0;
}
Jonathan Leffler
I appreciate the quick response Jonathan. 1) The reason I had them nonblocking for the moment is just so if a client tries to refresh the router table and there wasn't any data waiting on the pipe (there haven't been any new connections so the pipe hasn't been filled with a new number) it would just read nothing and then continue on. Do you think this may be contribute to my problem?It seems that the pipe is only updated once, at the fork. What am I missing?
Chris
+1  A: 

I'd suggest using a shared memory segment. Your parent and child processes can mmap the same file, and read/write state to it. This can be an actual file, or an anonymous memory segment. This is exactly what Apache does with their ScoreBoardFile.

brianegge
Not a bad idea - not least because it is easy to add an extra child process later. The only residual issues are (a) ensuring that the children aren't reading while the parent is writing, and (b) notifying the children when a change occurs.
Jonathan Leffler