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.

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

  1. Basic Forking: Compile and run the starter code above. Observe the process identifiers returned by the getpid() and getppid() functions.

  2. 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).

  3. 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.

  4. Execution Overwrite: Write two separate C programs, ex1.c and ex2.c. In ex1.c, use the execl() system call to execute the binary of ex2.c. Include a printf() statement in ex1.c immediately after the execl() call to prove whether or not it executes.

V. Common Pitfalls & Debugging Strategies

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


VIII. Resources & Further Reading