Assignment 02: Process Management and System Calls
I. Objective & Theoretical Framework
This assignment transitions your focus from user-space command execution to kernel-level process management. You will explore how an operating system spawns, manages, and terminates processes.
A process is a program in execution. In UNIX/Linux, the fork() system call is the primary mechanism to create a new process, known as a child process, under a parent process[cite: 1]. The child process starts its execution from the instruction immediately following the fork() call[cite: 1]. If a program makes $n$ fork() calls, $2^n$ processes will be created[cite: 1].
While fork() creates a copy of the parent, the exec() family of system calls (like execl() and execv()) is used to completely overwrite the memory space of a process with a new program[cite: 1].
II. Prerequisite Knowledge & Resources
Before beginning, ensure you are familiar with standard C compilation in a Linux environment using gcc.
- Required Headers:
<stdio.h>,<unistd.h>,<sys/types.h>,<sys/wait.h>. - Manual Pages: Run
man 2 fork,man 3 exec, andman 2 waitin your terminal.
III. Starter Code & Partial Implementations
When a call is made to fork(), a child process is created which gets a copy of the parent’s variables, but the fork() function returns a value of 0 to the child process[cite: 1]. To the parent process, fork() returns the value of the child’s PID[cite: 1].
Use the following skeleton code to understand the branching logic:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main() {
int pid = fork();
if (pid < 0) {
printf("Fork failed! Memory error.\n");
return 1;
} else if (pid == 0) {
// Child Process Block
printf("I am the child. My PID: %d\n", getpid());
printf("My parent's PID: %d\n", getppid());
} else {
// Parent Process Block
printf("I am the parent. My PID: %d\n", getpid());
printf("My child's PID is: %d\n", pid);
}
return 0;
}
IV. Step-by-Step Task List
Basic Forking: Compile and run the starter code above. Observe the process identifiers returned by the
getpid()andgetppid()functions.The Orphan Process: Create a program where the child prints the PID of its parent and itself, and then goes to sleep using
sleep(20). Meanwhile, have the parent print its details and immediately terminate. Observe that after 20 seconds, the child wakes up to find its parent terminated, becoming an “Orphan,” and is subsequently adopted by the process dispatcher (PID 1).The Zombie Process: Zombies are processes that have terminated but are not removed from the process table. Write a program where the parent process goes to sleep for 20 seconds, but the child process terminates immediately.
Execution Overwrite: Write two separate C programs,
ex1.candex2.c. Inex1.c, use theexecl()system call to execute the binary ofex2.c. Include aprintf()statement inex1.cimmediately after theexecl()call to prove whether or not it executes.
V. Common Pitfalls & Debugging Strategies
Shared Variables Misconception:
fork()creates two identical processes, but they are totally independent. They share the same variables logically, but each has its own independent copy in memory. Modifying a variable in the child does not alter it in the parent.Zombie Hunting: When testing for Zombie processes, use the
ps -lcommand in a separate terminal window while your parent process is sleeping. Look for a ‘Z’ in the second column to confirm the child is in a zombie state.Exec() Return Values: Remember that
execl()does not create an independent process; the PID of the old and new process remains the same. Any code that exists after a successfulexec()call will never get executed because the memory space is entirely overwritten.System-Level Verification: Do not just trust your
printfstatements. Run your compiled program in the background (e.g.,./a.out &), then useps -lin the same terminal to hunt for theZ(Zombie) orO(Orphan) status flags in the state column.System Call Tracing: Use the
stracecommand (e.g.,strace ./a.out). This powerful diagnostic tool intercepts and records the system calls which are called by a process, allowing you to see exactly how the kernel handles yourfork()andexec()requests under the hood.
VI. Real-World Case Study
Understanding process branching is critical for high-performance network engineering. For example, in telecommunications—such as managing concurrent connections and load balancing in dense LTE networks—a master daemon (the parent) continuously listens for incoming node requests. Instead of handling the request directly and blocking the queue, it uses fork() to spawn a dedicated child process for each specific data handover. Once the child completes the transaction, it terminates, while the parent remains unburdened, listening for the next connection.
VII. Advanced Variant Tasks
Dynamic Execution: Instead of separately specifying the called program along with its command line parameters using
execl(), rewrite your execution task using theexecv()call. Put your parameters into an array to prevent hard coding and bring flexibility for runtime adjustments.Wait and Reap: Modify your Zombie process program to use the
wait()orwaitpid()system call in the parent block. Prove that by having the parent wait for the child’s termination status, the child is successfully reaped and removed from the process table, preventing the Zombie state entirely.The Controlled “Fork Bomb” (Resource Limits & Security): Operating systems must protect themselves from rogue processes. Write a program containing a
while(1) { fork(); }loop. Run this only inside your isolated virtual machine. Watch the system freeze as the process table overflows. Reboot, then research and apply the Linuxulimit -ucommand to restrict your user’s maximum processes. Run the bomb again and observe how the OS now successfully contains the threat by blocking process creation.
VIII. Resources & Further Reading
- OSTEP (Operating Systems: Three Easy Pieces): This is the premier free OS textbook used globally. Read Chapter 5: The Process API (PDF) for exceptional visual diagrams of
fork()andexec(). - Linux Programmer’s Manual: Read the official man pages for fork(2) and execve(2). Pay special attention to the “Return Value” sections.