Child Process=fork()
-
fork() is internally implemented using clone()
Creates a new child process. Compelte |CS|DS|SS|HS| of parent is duplicated to child.
New Process control block is created for child. Code Segment is Duplicated using COW(Copy on Write)
-------------2. fork() Duplicates----------
| \/
PARENT CHILD
Stack Heap Data Segment Stack Heap Data Segment
1. | pPtr | "PARN" | g_a=1, g_b=0 | | pPtr | "PARN" | g_a=1, g_b=0 |
| /\
-------- 3. Child changes its values
Stack Heap Data Segment
| pPtr | "CHLD" | g_a=10, g_b=50 |
| /\
--------
4.
PARENT pPtr=PARN, g_a=1, g_b=0
CHILD pPtr=CLD, g_a=10, g_b=0
//Complete Tree
# pstree -hp |grep a.out
|-gnome-terminal-(4730)-+-bash(4737)---su(4886)---bash(4908)---a.out(10005)---a.out(10006)
Parent PID=10005 Child PID=10006
//PID of parent in child
pid_t ppid = getppid();
#include<unistd.h> //fork
#include<iostream>
#include<cstring>
//DS
int g_a = 1; //Initialized
int g_b; //Uninitialized
int main(){
string str; //Stack
char *p = new char[7]; //Heap
strcpy(p, "Parent");
std::cout << "[Before fork] g_a: " << g_a\
<< ", g_b: " << g_b\
<< ", p: " << p << std::endl;
int k = fork();
if (k==0){ //CHILD
g_a = 10; //DS
strcpy(p,"Child"); //Stack
std::cout << "\n[CHILD] g_a: " << g_a\
<< ", g_b: " << g_b\
<< ", p: " << p << std::endl;
}else{
std::cout << "Parent";
}
std::cout << "\n[AFTER fork] g_a: " << g_a\
<< ", g_b: " << g_b\
<< ", p:" << p << std::endl;
delete p;
return 0;
}
# ./a.out
[Before fork] g_a: 1, g_b: 0, pPtr: PARN
[PARENT]
[CHILD] g_a: 10, g_b: 0, pPtr: CHLD
[AFTER] g_a: 1, g_b: 0, pPtr: PARN //Notice Executed Twice, 1 for Parent, 1 for Child
[AFTER] g_a: 10, g_b: 0, pPtr: CHLD
clone
int clone(int (*fn)(void *), void *stack, int flags, void arg, .../ pid_t *parent_tid, void *tls, pid_t *child_tid */ );
-
API creates a new child process, in a manner similar to fork(2).
Unlike fork(2), the clone(2) allows the child process to share parts of its execution context with the calling process, such as the memory space, the table of file descriptors, and the table of signal handlers.
You can pass different namespace flags to clone(2) to create new namespaces for the child process.
COW(Copy on Write)
-
Initially, CS, DS, SS, HS all are shared between parent & child .
If any one of them(either parent or child) modifies the data, New page is created & modified data is copied
#include <unistd.h>
/*Code-1: Parent & child process executing same code
void main(){
fork();
printf("Hello");
}
Output
Hello //Child
Hello //Parent
Why? CS for parent and child are same
*/
/*Code-2: COW*/
int dsVar = 10; //DS variable
void main(){
int stackVar=10; //stack variable
int *heapVar = malloc(sizeof(int));
*heapVar=10; //Heap variable
if(fork() == 0){ //Child
stackVar = *heapVar = dsVar = 20; //Child CS Changed
printf("%d %d %d\n", stackVar, *heapVar, dsVar); // 20 20 20
}else{ //Parent
printf("%d %d %d\n", stackVar, *heapVar, dsVar); // 10 10 10
}
}
Output:
10 10 10
20 20 20
Code Examples
1 child of parent
#include<unistd.h>
int main(){
int ret;
ret = fork();
if (ret == 0){ //Child
printf ("Inside Child\n");
printf ("[Child] Parent's PID=%d\n", getppid()); //175. Parent's PID is always smaller since its created earlier.
printf ("[Child] Self PID=%d\n", getpid()); //176
} else { //Parent
printf ("Inside Parent\n");
printf ("[Parent] Parent's PID=%d\n", getppid()); //92
printf ("[Parent] Self PID=%d\n", getpid()); //175
}
}
$ ./a.out
Inside Parent
Inside Child
[Parent] Parent's PID=92
[Child] Parent's PID=175
[Parent] Self PID=175
[Child] Self PID=176
n forks
- if we call fork() n number of times. Common code is executed 2n times.
#include<unistd.h> //fork
int main(){
fork();
fork();
fork();
cout <lt; "Out\n"; //called 8 times
}
# ./a.out
Out
Out
Out
Out
Out
Out
Out
Out
fork()
/ \
---------fork() fork()-----------
| fork() fork() |
/ \ / \ /\ /\
2 Children of a parent
- To create 2 children we need to call fork inside parent.
#include <unistd.h>
void error(char* cString) {
perror(cString);
printf("%d", errno);
exit(0);
}
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <error.h>
#include <string>
#include <iostream>
extern int errno;
void error(std::string cString) {
//perror(cString);
printf("%d", errno);
exit(0);
}
int main() {
pid_t child_a, child_b;
child_a = fork();
if (child_a == 0) {
/* Child A code */
sleep(1);
std::cout << "Child1:" << getpid() << "\n";
} else {
child_b = fork();
std::cout << "Parent:" << getpid() << "\n";
if (child_b == 0) {
/* Child B code */
std::cout << "Child2:"<< getpid() << "\n";
} else {
/* Parent Code */
std::cout << "Parent:" << getpid() << "\n";
}
}
}
$ a.out
Child1: 1234
Parent: 780
Child2: 1235
Parent: 780