Home > Enterprise >  How do I get this fork() function to print once instead of multiple times in the for loop?
How do I get this fork() function to print once instead of multiple times in the for loop?

Time:10-06

It's my first time coding in C and working with forks, so it's a bit confusing to me. My assignment is to create 10 total processes = 1 parent process, 3 children processes, and 6 grandchildren processes. This is the grandchildren processes which I'm currently struggling with.

int i=0, j=0, child_count=1, grand_count=1;

    for (i = 0; i < 3; i  ) {
        pid_t child = 0;
        child = fork();
        
        if (child != 0) {            
            for (j = 0; j < 2; j  ) {
                pid_t grand = 0;
                grand = fork();

                if (grand == 0){
                    printf("7I am grand #%d with PID %d and PPID %d\n", grand_count, getpid(), getppid());
                    exit(0);
                }
                grand_count  ;
            }
        }
}

I need it to print:

I am grand #6 with PID 4373 and PPID 745

I am grand #5 with PID 4372 and PPID 745

I am grand #4 with PID 4370 and PPID 745

I am grand #3 with PID 4369 and PPID 745

I am grand #2 with PID 4367 and PPID 745

I am grand #1 with PID 4366 and PPID 745

But instead, I am getting:

I am grand #5 with PID 4372 and PPID 745

I am grand #4 with PID 4370 and PPID 745

I am grand #6 with PID 4373 and PPID 745

I am grand #3 with PID 4369 and PPID 745

I am grand #2 with PID 4367 and PPID 745

I am grand #1 with PID 4366 and PPID 745

I am grand #4 with PID 4376 and PPID 745

I am grand #3 with PID 4375 and PPID 745

I am grand #4 with PID 4382 and PPID 745

I am grand #3 with PID 4381 and PPID 745

I am grand #2 with PID 4379 and PPID 745

I am grand #1 with PID 4378 and PPID 745

I am grand #2 with PID 4385 and PPID 745

I am grand #1 with PID 4384 and PPID 745

It prints out of order (I'm assuming due to the for loops, but I'm unsure of how to fix that so any advice would be welcome). But I'm also unsure of how to prevent it from printing the same lines multiple times.

CodePudding user response:

  • Your condition if (child != 0) is wrong and the statement following it will be executed by the parent process. It should be if (child == 0)
  • You never exit the child processes which makes them also loop and create children. You should wait for the grand children to die and then exit the child processes too.
  • At the very end, wait for the children before you let the program terminate.
  • Counting grand children the way you do will not work. The grand_count variable is not shared between the processes. It's copied from the current parent process at every fork.

Example:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main() {
    for(int i = 0; i < 3; i  ) {
        pid_t child = fork();

        if(child == 0) {
            for(int j = 0; j < 2; j  ) {
                pid_t grand = fork();

                if(grand == 0) {
                    printf("I am grand child with PID %d and PPID %d\n",
                           getpid(), getppid());
                    exit(0);
                }
            }

            // collect grand children
            for(pid_t who_died; (who_died = wait(NULL)) != -1;) {
                printf("grand child %d died\n", who_died);
            }
            exit(0); // and exit child
        }
    }

    // collect children
    for(pid_t who_died; (who_died = wait(NULL)) != -1;) {
        printf("child %d died\n", who_died);
    }
}

Possible output:

I am grand child with PID 2120874 and PPID 2120872
I am grand child with PID 2120876 and PPID 2120873
I am grand child with PID 2120877 and PPID 2120872
I am grand child with PID 2120878 and PPID 2120875
I am grand child with PID 2120880 and PPID 2120873
I am grand child with PID 2120883 and PPID 2120875
grand child 2120874 died
grand child 2120877 died
grand child 2120880 died
grand child 2120876 died
grand child 2120878 died
grand child 2120883 died
child 2120873 died
child 2120875 died
child 2120872 died

CodePudding user response:

There's a few things wrong.

  • fork returns 0 for the child process. if (child != 0) { is running in the parent (or an error).
  • Each fork gets its own copy of all the variables. Each child is incrementing its own grand_count.
  • You're not waiting until the child processes are done.

fork can be very confusing. While getting used to it, I encourage using a basic scaffolding.

pid_t cpid = fork();
if (cpid == 0) {
    child_function();

    exit(0);
}
else if( cpid == -1 ) {
    fork_error_handler();
}

// parent code

By putting the child code into a void function we avoid nesting loops and forks. Nesting makes things complicated.

With that in mind, we can redo your outer loop to do nothing but spawn child processes and pass along the ID to start printing from.

void fork_error_handler() {
    perror("fork() failed");
}

void wait_all() {
    // wait(NULL) waits for any child process to exit.
    // If there are no more child processes, it returns -1.
    // So to wait for all children to exit, keep calling wait
    // until it returns -1.
    while( wait(NULL) != -1 );
}

int main() {
    int num_children = 2;

    // From 0 to 2...
    for (int i = 0; i <= num_children; i  ) {
        pid_t cpid = fork();

        if (cpid == 0) {
            // 1, 3, 5
            spawn_printers(i * num_children   1);

            exit(0);
        }
        else if( cpid == -1 ) {
            fork_error_handler();
        }
    }

    wait_all();

}

Spawn all the children, but put their code into a function. This avoids nested forks and nested loops.

We also make sure to wait until all our child processes are complete. Without this, the parent process my exit before the children. This could cause the children to be killed when the parent exits.

Now we can write a separate function which spawns the printing processes.

void spawn_printers(int starting_id) {
    for (int i = 0; i < 2; i  ) {
        int id = starting_id   i;

        pid_t cpid = fork();

        if (cpid == 0) {
            printf("I am grand #%d with PID %d and PPID %d\n", id, getpid(), getppid());
            exit(0);
        }
        else if( cpid == -1 ) {
            fork_error_handler();
        }
    }

    // Each child has to wait for its own children.
    wait_all();
}

These will print out of order. That's the nature of forking. If you need it to print in a particular order, ask another question about that.

  • Related