Search code examples
cforkpid

Get pid from brother process


I want to have a parent process and three child processes. I want these child processes to know the pids of the other child processes. The problem is that when I do fork and then I do it again, the second fork is also executed in the child process creating an extra process (or so I think). How could I solve it?

Thanks.


Solution

  • The parent should fork three times, the children should not fork. This way, the parent will know the pids of all three children.

    After the fork, you'll need some kind of separate communication channel by which the parent can communicate these pids to all children. A simple way would be to open a pipe (see pipe(2)) before forking each child, so the child inherits the pipe's file descriptor (at least the read end) and the parent keeps the write end. Then have the parent send the three pids down each pipe and close it.

    Example code (long, but that's the nature of C):

    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <unistd.h>
    
    #define NUM_CHILDREN 3
    
    /* Entry point for the child processes */
    int child_main(int pipe_read_end) {
      pid_t my_pid = getpid();
    
      /* Read child pids from pipe */
      int child_pids[NUM_CHILDREN];
      unsigned int bytes_read = 0;
      while (bytes_read < sizeof(child_pids)) {
        ssize_t result = read(pipe_read_end, ((unsigned char *) child_pids) + bytes_read, sizeof(child_pids) - bytes_read);
        if (result < 0) {
          perror("error reading from pipe");
          return 1;
        } else if (result == 0) {
          fprintf(stderr, "unexpected end of file\n");
          return 1;
        } else {
          bytes_read += result;
        }
      }
      close(pipe_read_end);
    
      /* Do something useful with these child pids */
      for (int i = 0; i < NUM_CHILDREN; i++) {
        printf("Child %d received sibling pid %d\n", my_pid, child_pids[i]);
      }
    
      return 0;
    }
    
    /* Entry point for the parent process. */
    int main() {
      int child_pids[NUM_CHILDREN];
      int pipe_write_ends[NUM_CHILDREN];
      for (int i = 0; i < NUM_CHILDREN; i++) {
        /* Create the pipe for child i */
        int pipefd[2];
        if (pipe(pipefd)) {
          perror("error creating pipe");
          return 1;
        }
        int pipe_read_end = pipefd[0];
        int pipe_write_end = pipefd[1];
    
        /* Fork child i */
        pid_t child_pid = fork();
        if (child_pid < 0) {
          perror("error forking");
          return 1;
        } else if (child_pid == 0) {
          printf("Child %d was forked\n", getpid());
          close(pipe_write_end);
          return child_main(pipe_read_end);
        } else {
          printf("Parent forked child %d\n", child_pid);
          close(pipe_read_end);
          pipe_write_ends[i] = pipe_write_end;
          child_pids[i] = child_pid;
        }
      }
    
      /* Send pids down the pipes for each child */
      for (int i = 0; i < NUM_CHILDREN; i++) {
        unsigned int bytes_written = 0;
        while (bytes_written < sizeof(child_pids)) {
          ssize_t result = write(pipe_write_ends[i], ((unsigned char *) child_pids) + bytes_written, sizeof(child_pids) - bytes_written);
          if (result < 0) {
            perror("error writing to pipe");
            return 1;
          } else {
            bytes_written += result;
          }
        }
        close(pipe_write_ends[i]);
      }
    
      /* Wait for children to exit */
      for (int i = 0; i < NUM_CHILDREN; i++) {
        if (waitpid(child_pids[i], 0, 0) < 0) {
          perror("error waiting for child");
          return 1;
        }
      }
    }
    

    As @PSkocik points out in their answer, you should probably not be doing this. Pids can be reused by the OS, so there's no way for the children to know that their sibling pids still actually refer to their siblings; only the parent can be sure, because it has to wait for each pid before it can be reused.

    However, this same mechanism can be used for other forms of IPC (inter-process communication); you could, for example, use it to create pipes between the children directly.