University of Central Punjab
FOIT (Operating Systems)
Topic to be covered
● Process Creation
○ fork ()
○ getpid()
○ getppid()
● Process Completion
○ wait (int *)
○ exit (int)
● Orphan Process
● Zombie Process
● Multiple forks in a program.
● Example Programs for proof of concept
Objectives
● Understanding the concept of creating new processes in Linux.
● Understanding the process id and parent process id using getpid() and getpid()
● Parent waiting for its child to terminate using wait() system call.
● Concept of orphan and zombie Processes.
● Understanding the use of multiple forks in a program using tree diagram.
Prerequisite:
● Visual Studio Code
● G++ compiler
● Basic c++ Programing
● Use of man Page
University of Central Punjab
FOIT (Operating Systems)
Process Creation in Linux:
In an operating system, the `fork()` system call is used to create a new process, which is essentially a duplicate
of the calling process. This new process is called the child process, while the original is the parent process. The
`fork()` function does not take any parameters and returns a `pid_t` type, representing the process ID.
After `fork()` is called, two processes will run concurrently. Both the parent and child processes will execute
the next instruction following the `fork()`. To differentiate between them, the return value of `fork()` must be
checked:
- If `fork()` returns a negative value, the creation of the child process failed.
- A return value of zero indicates that the current process is the child.
- A positive value indicates that the current process is the parent, and the returned value is the process ID of the
child.
The function `getpid()` can be used by a process to obtain its own process ID, which helps in identifying the
process uniquely. This mechanism is fundamental for multitasking in operating systems, allowing processes to
perform independent tasks.
pid_t fork()
University of Central Punjab
FOIT (Operating Systems)
Example 1: Simple Process Creation
#include <iostream>
#include <unistd.h>
using namespace std;
int main() {
pid_t pid = fork();
if (pid == 0) {
cout << "I am the child process." << endl;
} else {
cout << "I am the parent process." << endl;
}
return 0;
}
Task 1: Run the program, observe the output, and explain it.
Output:
?
Example 2: Fork with Error Handling
#include <iostream>
#include <unistd.h>
using namespace std;
int main() {
pid_t pid = fork();
if (pid < 0) {
cerr << "Fork failed!" << endl;
return 1;
} else if (pid == 0) {
cout << "Child process.\n";
} else {
cout << "Parent process.\n";
}
University of Central Punjab
FOIT (Operating Systems)
return 0;
}
Task 2: Run the program, observe the output, and explain it.
Output:
?
Understanding Process IDs
Every Linux process has a unique process ID (PID) that helps in identifying it. We can use `getpid()` to retrieve
the PID of the current process and `getppid()` to get the PID of the parent process.
Example 3: Displaying Process IDs
#include <iostream>
#include <unistd.h>
using namespace std;
int main() {
pid_t pid = fork();
if (pid == -1) {
cerr << "Fork failed!" << endl;
return 1;
}
if (pid == 0) {
cout << "Child Process. PID: " << getpid() << " Parent PID: " << getppid() <<
endl;
} else {
cout << "Parent Process. PID: " << getpid() << endl;
}
return 0;
}
Task 3: Run the program, observe the output, and explain it.
Output:
?
Example 4: Check Process ID Before and After Fork
University of Central Punjab
FOIT (Operating Systems)
#include <iostream>
#include <unistd.h>
using namespace std;
int main() {
cout << "Before fork. Current process PID: " << getpid() << endl;
pid_t pid = fork();
if (pid == 0) {
// Child process
cout << "After fork in child. Child PID: " << getpid() << ", Parent PID: " <<
getppid() << endl;
}
else {
// Parent process
cout << "After fork in parent. Parent PID: " << getpid() << endl;
}
return 0;
}
Task 4: Run the program, observe the output, and explain it.
Output:
?
Waiting for Child Process
The `wait()` system call makes the parent process wait until the child finishes its execution, ensuring
synchronized execution.
Example 5: Parent Waiting for Child Process
#include <iostream>
#include <unistd.h>
#include <sys/wait.h>
using namespace std;
int main() {
University of Central Punjab
FOIT (Operating Systems)
pid_t pid = fork();
if (pid == -1) {
cerr << "Fork failed!" << endl;
return 1;
}
if (pid == 0) {
cout << "Child Process. PID: " << getpid() << endl;
exit(0);
} else {
wait(NULL);
cout << "Parent Process. PID: " << getpid() << endl;
}
return 0;
}
Task 5: Run the program, observe the output, and explain it.
Output:
?
Process Completion:
The functions described in this section are used to wait for a child process to terminate or stop, and determine its status.
These functions are declared in the header file "sys/wait.h".
pid_t wait (int * status)
wait() will force a parent process to wait for a child process to stop or terminate. wait() return the pid of the child or
-1 for an error. The exit status of the child is returned to status.
void exit (int status)
exit() terminates the process which calls this function and returns the exit status value. Both UNIX and C (forked)
programs can read the status value.
Example-06: Parent waiting for child to terminate.
#include <iostream>
#include <unistd.h> // for fork(), getpid()
#include <cstdlib> // for exit()
#include <sys/wait.h> // for wait()
University of Central Punjab
FOIT (Operating Systems)
using namespace std;
int main() {
pid_t cpid = fork(); // Fork to create a child process
if (cpid == -1) {
cerr << "Fork failed" << endl;
exit(1); // Exit with error code 1 if fork fails
}
if (cpid == 0) {
// Child process
cout << "Hello, I am the Child, and my PID is: " << (long)getpid() << endl;
exit(0); // Child process exits
} else {
// Parent process
wait(NULL); // Wait for the child to terminate
cout << "Hello, I am the Parent, and my PID is: " << (long)getpid() << endl;
}
return 0;
}
Task 6: Run the program, observe the output, and explain it.
Output:
?
Orphan processes:
When a parent dies before its child, the child is automatically adopted by the original “init” process
whose PID is 1. To illustrate this insert a sleep statement into the child’s code. This ensured that
the parent process terminated before its child.
Example-07: Creating Orphan Process.
#include <iostream>
#include <sys/wait.h> // For wait
#include <unistd.h> // For fork, getpid, sleep
#include <cstdlib> // For exit
using namespace std;
int main(int argc, char* argv[]) {
University of Central Punjab
FOIT (Operating Systems)
pid_t pid;
cout << "I am the original process with PID " << getpid() << " and PPID "
<< getppid() << endl;
pid = fork(); // Fork a new process
if (pid > 0) {
// Parent process
cout << "I am the parent with PID " << getpid() << " and PPID " <<
getppid() << endl;
cout << "My child's PID is " << pid << endl;
}
else if (pid == 0) {
// Child process
sleep(4); // Sleep for 4 seconds to allow orphan creation
cout << "I'm the child with PID " << getpid() << " and PPID " <<
getppid() << endl;
exit(0); // Child process terminates
}
cout << "PID " << getpid() << " terminates." << endl;
return 0;
}
Task 7: Run the program, observe the output, and explain it.
Output:
?
Zombie processes:
A process that terminates cannot leave the system until its parent accepts its return code. If its parent
process is already dead, it’ll already have been adopted by the “init” process, which always accepts its
children’s return codes. However, if a process’s parent is alive but never executes a wait ( ), the
process’s return code will never be accepted and the process will remain a zombie.
Example-08: Creating Zombie Process
#include <iostream>
#include <unistd.h> // For fork and sleep
University of Central Punjab
FOIT (Operating Systems)
#include <cstdlib> // For exit()
using namespace std;
int main() {
pid_t pid;
pid = fork(); // Create a new process
if (pid > 0) {
// Parent process: pid is non-zero
cout << "I am the parent process. Going into an infinite sleep loop..." <<
endl;
while (1) {
sleep(100); // Parent sleeps for 100 seconds in an infinite loop
}
}
else if (pid == 0) {
// Child process: pid is zero
cout << "I am the child process. Exiting now..." << endl;
exit(0); // Child exits immediately
}
return 0;
}
Task 8: Run the above program and understand the output. See the status of both processes using “ps -
a”. How the child can be terminated?
Output:
?
Example-09: Describes what happens when fork() is called multiple times
#include <iostream>
#include <unistd.h> // For fork()
using namespace std;
int main() {
fork(); // First fork
fork(); // Second fork
fork(); // Third fork
University of Central Punjab
FOIT (Operating Systems)
cout << "Hello fork..." << endl;
return 0;
}
Task-09: Compile and run above program and discuss the output with tree diagram:
Output:
?
Description With Tree Diagram:
Example-10: Describes what happens when fork() is called multiple times
using &&.
#include <iostream>
#include <unistd.h> // For fork()
using namespace std;
int main() {
fork() && fork(); // First fork and conditionally a second fork
cout << "UCP" << endl; // Output "UCP"
return 0;
}
Task-10: Compile and run above program and discuss the output with tree diagram:
Output:
?
Description With Tree Diagram:
Example-11: Describes what happens when fork() is called multiple times
using ||.
#include <iostream>
#include <unistd.h> // For fork()
using namespace std;
int main() {
University of Central Punjab
FOIT (Operating Systems)
fork() || fork(); // First fork and conditionally a second fork
cout << "UCP" << endl; // Output "UCP"
return 0;
}
Task-11: Compile and run above program and discuss the output with tree diagram:
Output:
?
Description With Tree Diagram: ?
University of Central Punjab
FOIT (Operating Systems)