The child processes inherit/copy a lot of resources, memory, code and handles from the parent process. For this reason the sleep(1) and the printf() that follows it is executed by all branches of your code, both by the parent and the two children. If you want to execute it only by the original parent process after spawning both children then you have to move the sleep along with the printf into an else branch of the inner if:
if(child2==0)
{
printf("This is child2 process and pid is %d\n",getpid());
} else {
sleep(1);
printf("The parent process has pid %d\n",getppid());
}
There it is executed only by the original parent after spawning both children. You can create grandchildren by forking() once in the child process. You can create a great child by forking once in a grand child process... But remember, a lot of resources and all the code is copied by every forked children (like your sleep and printf) and the execution flow goes as the language flow constructs dictate!
Here is a piece of code that spawns the following process hierarchy:
parent
+--child
+--grandchild1
+--grandchild2
This is a good example of how not to write a program. This is a big piece of disgusting spaghetti code where many bugs can hide easily.
#include<stdio.h>
void variation1()
{
pid_t child, grandchild1, grandchild2;
int child_status, grandchild1_status, grandchild2_status;
printf("The parent process has pid %d\n", getpid());
child = fork();
if(child == 0)
{
printf("This is a child process and pid is %d\n", getpid());
grandchild1 = fork();
if (grandchild1 == 0)
{
printf("This is grandchild1 and pid is %d\n", getpid());
sleep(1);
exit(5);
}
else if (grandchild1 > 0)
{
grandchild2 = fork();
if (grandchild2 == 0)
{
printf("This is grandchild2 and pid is %d\n", getpid());
sleep(1);
exit(6);
}
else if (grandchild2 > 0)
{
waitpid(grandchild2, &grandchild2_status, 0);
if (WIFEXITED(grandchild2_status))
printf("grandchild2 has exited with exit code %d\n", WEXITSTATUS(grandchild2_status));
else
printf("grandchild2 has terminated abnormally\n");
}
else
{
printf("Error forking grandchild2!\n");
}
waitpid(grandchild1, &grandchild1_status, 0);
if (WIFEXITED(grandchild1_status))
printf("grandchild1 has exited with exit code %d\n", WEXITSTATUS(grandchild1_status));
else
printf("grandchild1 has terminated abnormally\n");
}
else
{
printf("Error forking grandchild1!\n");
}
exit(0);
}
else if (child > 0)
{
waitpid(child, &child_status, 0);
if (WIFEXITED(child_status))
printf("child has exited with exit code %d\n", WEXITSTATUS(child_status));
else
printf("child has terminated abnormally\n");
}
else
{
printf("Error forking child!\n");
}
}
Here is a piece of code that should do nearly the same but it is much cleaner and easier to understand. It is designed in a much better way and its easier to modify/maintain without introducing bugs:
typedef int (*child_func_pointer_t)();
pid_t fork_child_in_function(child_func_pointer_t child_func, const char* child_name)
{
pid_t child_pid = fork();
if (child_pid == 0)
{
printf("Hello! I'm child %s and I've just started with pid=%d (parent_pid=%d)\n", child_name, getpid(), getppid());
exit(child_func());
}
if (child_pid < 0)
printf("Error forking %s!\n", child_name);
return child_pid;
}
void wait_child(pid_t child_pid, const char* child_name)
{
int child_status;
waitpid(child_pid, &child_status, 0);
if (WIFEXITED(child_status))
printf("%s has exited with exit code %d\n", child_name, WEXITSTATUS(child_status));
else
printf("%s has terminated abnormally\n", child_name);
}
void fork_and_wait_child(child_func_pointer_t child_func, const char* child_name)
{
pid_t child_pid = fork_child_in_function(child_func, child_name);
if (child_pid > 0)
wait_child(child_pid, child_name);
}
int grandchild1_func()
{
sleep(1);
return 5;
}
int grandchild2_func()
{
sleep(1);
return 6;
}
int child_func()
{
int grandchild1_pid, grandchild2_pid;
grandchild1_pid = fork_child_in_function(grandchild1_func, "grandchild1");
grandchild2_pid = fork_child_in_function(grandchild2_func, "grandchild2");
if (grandchild1_pid > 0)
wait_child(grandchild1_pid, "grandchild1");
if (grandchild2_pid > 0)
wait_child(grandchild2_pid, "grandchild2");
return 0;
}
void variation2()
{
fork_and_wait_child(child_func, "child");
}
I haven't tested any of these codepieces but this is a very good "pseudo code" that shows how to do things well and shows the difference between junk code and well designed code.