using fork() to make 3 children out of 1 parent in C (not C++)

23,061

Solution 1

The parent needs to
- print a message
- call fork three times
- wait for the three children to exit

Each child needs to
- print a message
- exit

So the code is as simple as

int main( void )
{
    printf( "[dad] pid %d\n", getpid() );

    for ( int i = 0; i < 3; i++ )
        if ( fork() == 0 )
        {
            printf( "[son] pid %d from pid %d\n", getpid(), getppid() );
            exit( 0 );
        }

    for ( int i = 0; i < 3; i++ )
        wait( NULL );
}

which generates this output

[dad] pid 1777
[son] pid 1778 from pid 1777
[son] pid 1779 from pid 1777
[son] pid 1780 from pid 1777

Solution 2

The one thing you have to remember is that, when you fork, both the parent and child will continue on running the code at that point.

So, if you don't do child/parent detection correctly, the children will most likely start up their own children.

One good way for you to start up three children and no grandchildren is to use a counter in conjunction with the returned process ID from the fork call, along the lines of the following:

#include <stdio.h>
#include <unistd.h>

#define COUNT 3

int main(void) {
    # Desired and actual count.

    int count = COUNT, children = 0;

    // Force parent initially.

    pid_t retpid = 1;

    // Only fork if limit not reached AND is parent (children
    //   will exit loop with retpid == 0).

    while (count-- > 0 && retpid > 0)
        // Adjust actual count if successful.

        if ((retpid = fork()) > 0)
            children++;

    // Detect parent, all forks returned non-zero.

    if (retpid != 0) {
        printf("Parent %d spawned %d/%d children\n",
            getpid(), children, COUNT);

        // Wait for children to finish.

        while (children-- > 0)
            wait(NULL);
    } else {
        // Otherwise you were one of the children.

        printf("Child %d, sired by %d\n", getpid(), getppid());
    }

    return 0;
}

This outputs what you seem to be after though, due to the vagaries of scheduling, not necessarily in that order:

Parent 26210 successfully spawned 3/3 children
Child 26212, sired by 26210
Child 26213, sired by 26210
Child 26211, sired by 26210

The checking of the returned PID will ensure only the parent does any forking, and the count will limit it to a specific quantity.


One thing you also need to watch out for is output buffering. When you fork, you may end up with two processes with buffered output data.

Where the output device can be detected to be a terminal, flushing will normally occur on a newline being output, so your printf calls probably won't duplicate output for a normal run.

You just need to be aware that you may get interesting results if you redirect your output to a file, for example.

Share:
23,061
Jite
Author by

Jite

Updated on September 29, 2020

Comments

  • Jite
    Jite over 3 years

    Hi there I've been working on a program that forks children and later will fork more children from each child but that's not what I need help with. When I run my program (in here it is a function but works the same) I am supposed to have one parent(PPID) spawn 3 children (PIDS= 1,2,3) but what I get is either the same PID and PPID 3 times (my current code) or before I was getting 3 parents with each parent having one child and the PPIDS were different as well as the PIDS, but the PPIDs were just the same as the previous child PIDs. In my latest attempts it never displays the parent(dad) message above the child(son). It should look like this

    [dad] hi am I PID 1234 and I come from ####(dont care what this number is)
    [son] hi i am PID 1111 and I come from PPID 1234
    [son] hi i am PID 1112 and I come from PPID 1234
    [son] hi i am PID 1113 and I come from PPID 1234
    

    here is my code. I'm just looking for hints if possible unless it's just a silly mistake I've made like "oh just move the fork() to the child process" or something like that.

    Also I have a child_count just so I can easily count the children.

     int forking(null)
    {
           void about(char *);
            int i=0;
            int j=0;
            int child_count =0;
            about("dad");
    
        for(i = 0; i < 3; i++ ){
            pid_t child = 0;
            child = fork();
    
    
                if (child < 0) { //unable to fork error
                        perror ("Unable to fork");
                        exit(-1);}
    
               else if (child == 0){ //child process
                        about ("son");
                        printf("I am child #%d \n",child_count);
                        child_count++;
                        exit(0);}
    
                else { //parent process (do nothing)
    
                    }
                }
    
                    for(j = 0; j < 3; j++ ){
                                wait(NULL);//wait for parent to acknowledge child process
                                }
    return 0;
    }
    
    • Some programmer dude
      Some programmer dude over 8 years
      Two things to remember: The first is that once you have forked, the child process get a copy of the memory of the parent, and when the child modifies e.g. variables those variables are changed only in the child, the parent (or any "siblings") won't see those variable changes. The second thing is that you call to wait in the else case will block both parent and child.
    • Some programmer dude
      Some programmer dude over 8 years
      Oh, and you have a bad case of undefined behavior in your code. You define the child variable, but then you use it before you initialize it. Uninitialized local variables have an indeterminate value, using them without initialization leads to UB. You might want to think about where you call fork.
    • Jite
      Jite over 8 years
      oh you mean like pid_t child =0? What do you mean wait() blocks both parent and child? And about the copy of memory could you give me an example code? It makes it easier to see that point.
    • Some programmer dude
      Some programmer dude over 8 years
      Regarding wait, you call it unconditionally after the call to fork, so it will be called in both the parent and the child process. If you initialize child to zero, what path will the code take in your condition? And process don't share memory, memory in a process is for that process only, and that memory includes variables like child_count, so modifying a variable in one process won't cause it to be modified in any other process. And again, think about where you do or should do the fork call.
    • user3386109
      user3386109 over 8 years
      The while loop at the end needs to be for(i=0;i<3;i++) loop. If you put a printf in the body of the while loop you'll see that it never runs because i is already 3 due to the preceding for loop.
    • Jite
      Jite over 8 years
      ahh thanks user3386109. I misread somewhere along the way that wait needs a while loop but it can be any loop