Home > Back-end >  Using fork() and execlp() to create process hierarchy in C
Using fork() and execlp() to create process hierarchy in C

Time:02-12

I MUST use fork() and execlp() to create and annotate the given process hierarchy: process hierarchy

I cannot wrap my head around when each process should be forked in order to reflect this hierarchy, add to that the non-negotiable use of execlp() which replaces the current process image.

Here is what I managed to come up with (please excuse the very non-DRY code, I'm new to these concepts):

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

#define oops(m) {perror(m); exit(EXIT_FAILURE);}

int main() {
    pid_t pid1_1, pid1_2, pid1_1_1, pid1_1_2, pid1_2_1, pid1_2_2;

    pid1_1 = fork();

    if (pid1_1 < 0) {
        oops("Fork Failed!");
    }

    // child 1.1
    if (pid1_1 == 0) {
        printf("I am the child %d\n", getpid());
        if (execlp("./iam", "iam", "1.1", NULL) < 0)
        oops("Execlp Failed!");

    } else {
        // grandchild 1.1.1
        pid1_1_1 = fork();
        if (pid1_1_1 < 0) {
            oops("Fork Failed!");
        }
        if (pid1_1_1 == 0) {
            printf("I am the grandchild %d\n", getpid());
            if (execlp("./iam", "iam", "1.1.1", NULL) < 0)
            oops("Execlp Failed!");
        }
        //grandchild 1.1.2
        pid1_1_2 = fork();
        if (pid1_1_2 < 0) {
            oops("Fork Failed!");
        }
        if (pid1_1_2 == 0) {
            printf("I am the grandchild %d\n", getpid());
            if (execlp("./iam", "iam", "1.1.2", NULL) < 0)
            oops("Execlp Failed!");
        }
    }

    pid1_2 = fork();

    if (pid1_2 < 0) {
        oops("Fork Failed!");
    }
    // child 1.2
    if (pid1_2 == 0) {
        printf("I am the child %d\n", getpid());
        if (execlp("./iam", "iam", "1.2", NULL) < 0)
        oops("Execlp Failed!");
    } else {
        // grandchild 1.2.1
        pid1_2_1 = fork();
        if (pid1_2_1 < 0) {
            oops("Fork Failed!");
        }
        if (pid1_2_1 == 0) {
            printf("I am the grandchild %d\n", getpid());
            if (execlp("./iam", "iam", "1.2.1", NULL) < 0)
            oops("Execlp Failed!");
        }
        // grandchild 1.2.2
        pid1_2_2 = fork();
        if (pid1_2_2 < 0) {
            oops("Fork Failed!");
        }
        if (pid1_2_2 == 0) {
            printf("I am the grandchild %d\n", getpid());
            if (execlp("./iam", "iam", "1.2.2", NULL) < 0)
            oops("Execlp Failed!");
        }
    }

    // pid > 0 ==> must be parent
    printf("I am the parent %d\n", getpid());
    /* parent will wait for the child to complete */
    if (waitpid(-1, NULL, 0) < 0)
        printf("-1 from wait() with errno = %d\n", errno);

    printf("Child terminated; parent exiting\n");
    exit(EXIT_SUCCESS);
}

My output shows that this hierarchy is not set up correctly. For example, manually stepping through with gdb and finishing the PID for 1.2 terminates the entire process tree (when 1.1 sub-tree should be left in tact).

Any suggestions for where I'm going wrong with logically replicating this process hierarchy would be really appreciated. Thanks!

CodePudding user response:

Any suggestions for where I'm going wrong with logically replicating this process hierarchy would be really appreciated.

Check this part of code at start of your program:

pid1_1 = fork();

this will fork a child process. After this you are doing:

    if (pid1_1 == 0) {
        printf("I am the child %d\n", getpid());
        if (execlp("./iam", "iam", "1.1", NULL) < 0)
        ......

That means, now the child process image will be replaced by another process image.
As per picture you have shown, a process is supposed to fork 2 child process before calling execlp(), if it is a parent process in the given process tree. Similar kind of problems are there in below part of your code.

I cannot wrap my head around when each process should be forked in order to reflect this hierarchy, .....

Look at the process tree closely and you will find it as a perfect binary tree where every internal node has 2 child and all leaf nodes are at same level.

That said, every process should create 2 child process and then call execlp() and as soon as you reach to the given height (which is 2 in your case), no child process should fork further.

I will show you how to create the process hierarchy and you can add the execlp() call to replace the current process image with some other process image.

add to that the non-negotiable use of execlp() which replaces the current process image.

I believe, here, the current process means the process which is forking of child processes and this includes the top most process (equivalent to root in tree) as well.

To create the hierarchy of process as perfect binary tree, you can do:

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

int main (int argc, char *argv[]) {

    int height;

    if (argc != 2) {
        printf ("Invalid number of arguments, exiting..\n");
        exit (0);
    }

    height = atoi (argv[1]);

    if (height < 0) {
        printf ("Invalid input.\n"); // error handling can be better
        exit (0);
    }

    printf ("Parent process, my pid = %d, height = %d\n", getpid(), height);

    for (int i = 0; i < height;   i) {
        printf ("\nMy pid : %d, current height of tree : %d, forking..\n", getpid(), i);

        pid_t pid = fork();

        if (pid == -1) { 
            printf ("Fork failed\n");
        } else if (pid == 0) {
            printf ("My pid = %d, [my parent : %d], I am child 1..\n", getpid(), getppid());

            // this sleep is for sequenced output, otherwise it's not needed
            // sleeping for 1 second 
            sleep (1);
            continue;
        }
   
        pid = fork();

        if (pid == -1) { 
            printf ("Fork failed\n");
        } else if (pid == 0) {
            printf ("My pid = %d, [my parent : %d], I am child 2..\n", getpid(), getppid());

            // this sleep is for sequenced output, otherwise it's not needed
            // sleeping for 1 second 
            sleep (1);
            continue;
        }

        // break the loop as the current process is done with forking 2 child process
        break;
    }

    // ADD execlp call here 

    // This part of code is to just show you the hierarchy.
    // If you add execlp call above then part is not needed.
    while (wait(NULL) > 0);
    printf ("pid %d : I am EXITING\n", getpid());

    // added sleep for sequenced output, otherwise it's not needed
    sleep (1);
    return 0;
}

Usage: ./a.out <height_of_process_tree>

Output:

# ./a.out 0            
Parent process, my pid = 50807, height = 0
pid 50807 : I am EXITING

# ./a.out 1
Parent process, my pid = 50808, height = 1

My pid : 50808, current height of tree : 0, forking..
My pid = 50809, [my parent : 50808], I am child 1..
My pid = 50810, [my parent : 50808], I am child 2..
pid 50810 : I am EXITING
pid 50809 : I am EXITING
pid 50808 : I am EXITING

# ./a.out 2
Parent process, my pid = 50811, height = 2

My pid : 50811, current height of tree : 0, forking..
My pid = 50812, [my parent : 50811], I am child 1..
My pid = 50813, [my parent : 50811], I am child 2..

My pid : 50812, current height of tree : 1, forking..

My pid : 50813, current height of tree : 1, forking..
My pid = 50814, [my parent : 50812], I am child 1..
My pid = 50815, [my parent : 50813], I am child 1..
My pid = 50816, [my parent : 50812], I am child 2..
My pid = 50817, [my parent : 50813], I am child 2..
pid 50814 : I am EXITING
pid 50815 : I am EXITING
pid 50816 : I am EXITING
pid 50817 : I am EXITING
pid 50812 : I am EXITING
pid 50813 : I am EXITING
pid 50811 : I am EXITING
  • Related