Skip to main content
โšก Calmops

Process Management Deep Dive: From Creation to Termination

Introduction

Every running program on your computer is a process. Whether it’s your web browser, a Python script, or a system daemonโ€”processes are the fundamental units of execution in any operating system. Understanding how processes work is essential for debugging issues, optimizing performance, and building robust software.

In this comprehensive guide, we’ll explore everything about process management from both theoretical and practical perspectives.


What is a Process?

Formal Definition

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                     PROCESS DEFINITION                           โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                                  โ”‚
โ”‚   A process is an instance of a program in execution.           โ”‚
โ”‚                                                                  โ”‚
โ”‚   It consists of:                                               โ”‚
โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚   โ”‚                                                         โ”‚   โ”‚
โ”‚   โ”‚   1. Program Code (Text Section)                       โ”‚   โ”‚
โ”‚   โ”‚      - The actual instructions to execute              โ”‚   โ”‚
โ”‚   โ”‚                                                         โ”‚   โ”‚
โ”‚   โ”‚   2. Program Data (Data Section)                       โ”‚   โ”‚
โ”‚   โ”‚      - Global variables, static data                   โ”‚   โ”‚
โ”‚   โ”‚                                                         โ”‚   โ”‚
โ”‚   โ”‚   3. Program Stack                                     โ”‚   โ”‚
โ”‚   โ”‚      - Function calls, local variables                 โ”‚   โ”‚
โ”‚   โ”‚                                                         โ”‚   โ”‚
โ”‚   โ”‚   4. Process Control Block (PCB)                       โ”‚   โ”‚
โ”‚   โ”‚      - Metadata and state information                 โ”‚   โ”‚
โ”‚   โ”‚                                                         โ”‚   โ”‚
โ”‚   โ”‚   5. Resources                                         โ”‚   โ”‚
โ”‚   โ”‚      - Open files, memory, child processes            โ”‚   โ”‚
โ”‚   โ”‚                                                         โ”‚   โ”‚
โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ”‚                                                                  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Process vs Program

Aspect Program Process
Definition Passive (file on disk) Active (in execution)
Lifetime Permanent Temporary
Components Code only Code + Data + Stack + PCB
Memory No Yes (allocated by OS)

Process Control Block (PCB)

The kernel maintains a data structure called the Process Control Block (PCB) for each process. This is the heart of process management.

PCB Structure

// Simplified PCB structure (Linux-like)
struct process_control_block {
    // Process identification
    pid_t pid;                    // Process ID
    pid_t parent_pid;             // Parent process ID
    uid_t user_id;                // Owner user ID
    gid_t group_id;               // Owner group ID
    
    // Process state
    enum process_state state;     // NEW, READY, RUNNING, BLOCKED, TERMINATED
    int exit_status;              // Exit code if terminated
    
    // Scheduling information
    int priority;                 // Scheduling priority
    unsigned long runtime;        // Time executed
    unsigned long start_time;     // When started
    unsigned long sleep_time;     // Time sleeping
    
    // Memory management
    void *virtual_memory;         // Address space
    unsigned long total_mem;      // Memory size
    struct mm_struct *mm;         // Memory descriptor
    
    // File descriptors
    struct file *filp[NO_FILE];   // Open files
    struct fs_struct *fs;         // File system info
    
    // Signal handling
    sigset_t signal_mask;         // Blocked signals
    struct sigaction sigaction[64]; // Signal handlers
    
    // Process credentials
    struct cred *cred;            // Security context
    
    // Inter-process communication
    struct ipc_namespace *nsproxy; // IPC namespaces
    
    // Parent-child relationship
    struct list_head children;    // Child processes
    struct list_head sibling;     // Sibling linkage
    
    // CPU context (for context switching)
    struct cpu_context ctx;        // Register state
    unsigned long sp;             // Stack pointer
    unsigned long pc;             // Program counter
};

PCB in Memory

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚              PROCESS CONTROL BLOCK LAYOUT                        โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                                  โ”‚
โ”‚   Kernel Space                                                    โ”‚
โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚   โ”‚                                                         โ”‚   โ”‚
โ”‚   โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”           โ”‚   โ”‚
โ”‚   โ”‚   โ”‚           Process A PCB                  โ”‚           โ”‚   โ”‚
โ”‚   โ”‚   โ”‚  PID: 1001                              โ”‚           โ”‚   โ”‚
โ”‚   โ”‚   โ”‚  State: RUNNING                         โ”‚           โ”‚   โ”‚
โ”‚   โ”‚   โ”‚  PC: 0x00401234                        โ”‚           โ”‚   โ”‚
โ”‚   โ”‚   โ”‚  SP: 0x7fff0000                        โ”‚           โ”‚   โ”‚
โ”‚   โ”‚   โ”‚  Priority: 20                          โ”‚           โ”‚   โ”‚
โ”‚   โ”‚   โ”‚  ...                                   โ”‚           โ”‚   โ”‚
โ”‚   โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜           โ”‚   โ”‚
โ”‚   โ”‚                                                         โ”‚   โ”‚
โ”‚   โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”           โ”‚   โ”‚
โ”‚   โ”‚   โ”‚           Process B PCB                  โ”‚           โ”‚   โ”‚
โ”‚   โ”‚   โ”‚  PID: 1002                              โ”‚           โ”‚   โ”‚
โ”‚   โ”‚   โ”‚  State: READY                           โ”‚           โ”‚   โ”‚
โ”‚   โ”‚   โ”‚  ...                                   โ”‚           โ”‚   โ”‚
โ”‚   โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜           โ”‚   โ”‚
โ”‚   โ”‚                                                         โ”‚   โ”‚
โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ”‚                                                                  โ”‚
โ”‚   User Space                                                      โ”‚
โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚   โ”‚   Process A Memory                                       โ”‚   โ”‚
โ”‚   โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”           โ”‚   โ”‚
โ”‚   โ”‚   โ”‚ Stack (grows down)  โ† SP                โ”‚           โ”‚   โ”‚
โ”‚   โ”‚   โ”‚ ...                                   โ”‚           โ”‚   โ”‚
โ”‚   โ”‚   โ”‚ Heap (grows up)                       โ”‚           โ”‚   โ”‚
โ”‚   โ”‚   โ”‚ Uninitialized data (BSS)              โ”‚           โ”‚   โ”‚
โ”‚   โ”‚   โ”‚ Initialized data                     โ”‚           โ”‚   โ”‚
โ”‚   โ”‚   โ”‚ Text (code) โ† PC                      โ”‚           โ”‚   โ”‚
โ”‚   โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜           โ”‚   โ”‚
โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ”‚                                                                  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Process States

A process can be in several states throughout its lifetime.

State Transition Diagram

                          โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                          โ”‚                 โ”‚
                          โ”‚   NEW (Created)  โ”‚
                          โ”‚                 โ”‚
                          โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                   โ”‚ fork()
                                   โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  scheduled   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   time slice   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚              โ”‚  โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ”‚                 โ”‚  โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€  โ”‚              โ”‚
โ”‚   RUNNING    โ”‚              โ”‚     READY       โ”‚                โ”‚   RUNNING    โ”‚
โ”‚              โ”‚ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ โ”‚                 โ”‚ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ โ”‚              โ”‚
โ”‚ (executing)  โ”‚   preempted  โ”‚ (waiting for    โ”‚    scheduled   โ”‚ (executing)  โ”‚
โ”‚              โ”‚              โ”‚   CPU)          โ”‚                โ”‚              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜              โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
       โ–ฒ                                                                      โ”‚
       โ”‚                                                                      โ”‚
       โ”‚       wait()              โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                         โ”‚
       โ”‚  โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ”‚                 โ”‚                         โ”‚
       โ”‚                          โ”‚    BLOCKED      โ”‚                         โ”‚
       โ”‚  I/O complete           โ”‚  (waiting for   โ”‚ โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
       โ”‚  signal received         โ”‚   event)        โ”‚    interrupt/wakeup
       โ”‚                          โ”‚                 โ”‚
       โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
       
                                   
       โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
       โ”‚                  โ”‚
       โ”‚   TERMINATED     โ”‚
       โ”‚   (Zombie)       โ”‚ โ—„โ”€โ”€ exit()
       โ”‚                  โ”‚
       โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                โ”‚ wait() collected by parent
                โ–ผ
       โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
       โ”‚                  โ”‚
       โ”‚   EXIT_DEAD      โ”‚
       โ”‚   (Reaped)       โ”‚
       โ”‚                  โ”‚
       โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Detailed State Descriptions

# Process state definitions (Linux kernel)
class ProcessState:
    """Linux process states."""
    
    # Actual states
    RUNNING = "R"      # Running or runnable (on run queue)
    SLEEPING = "S"     # Interruptible sleep (waiting for event)
    DISK_SLEEP = "D"   # Uninterruptible sleep (disk I/O)
    STOPPED = "T"      # Stopped by signal
    TRACING = "t"      # Stopped by debugger
    ZOMBIE = "Z"       # Dead process (exit status not collected)
    DEAD = "X"         # Dead (will be removed)
    
    # Additional states
    IDLE = "I"         # Idle kernel thread
    WAKEKILL = "K"     # Wakeup killed
    WAKING = "W"       # Waking
    PARKED = "P"       # Parked

def get_process_state_string(state_code):
    """Convert state code to readable string."""
    states = {
        'R': 'Running/Runnable',
        'S': 'Sleeping (Interruptible)',
        'D': 'Disk Sleep (Uninterruptible)',
        'T': 'Stopped',
        't': 'Tracing',
        'Z': 'Zombie',
        'X': 'Dead',
    }
    return states.get(state_code, 'Unknown')

Process Creation

The fork() System Call

In Unix-like systems, processes are created using the fork() system call.

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    FORK() MECHANISM                              โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                                  โ”‚
โ”‚   Parent Process                                                 โ”‚
โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”               โ”‚
โ”‚   โ”‚                                             โ”‚               โ”‚
โ”‚   โ”‚   pid = fork();  โ—„โ”€โ”€ System Call           โ”‚               โ”‚
โ”‚   โ”‚          โ”‚                                  โ”‚               โ”‚
โ”‚   โ”‚          โ”‚                                  โ”‚               โ”‚
โ”‚   โ”‚          โ–ผ                                  โ”‚               โ”‚
โ”‚   โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    returns TWICE            โ”‚               โ”‚
โ”‚   โ”‚   โ”‚ pid > 0  โ”‚ โ”€โ”€โ–บ Parent: returns child PIDโ”‚              โ”‚
โ”‚   โ”‚   โ”‚ pid == 0 โ”‚ โ”€โ”€โ–บ Child: returns 0        โ”‚               โ”‚
โ”‚   โ”‚   โ”‚ pid < 0  โ”‚ โ”€โ”€โ–บ Error: fork failed       โ”‚               โ”‚
โ”‚   โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                             โ”‚               โ”‚
โ”‚   โ”‚                                             โ”‚               โ”‚
โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜               โ”‚
โ”‚                                                                  โ”‚
โ”‚   Result: Two identical processes (copy-on-write)               โ”‚
โ”‚                                                                  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Fork Implementation

// Simplified fork() implementation
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int main() {
    pid_t pid = fork();
    
    if (pid < 0) {
        // Fork failed
        perror("fork failed");
        return 1;
    }
    
    if (pid == 0) {
        // Child process
        printf("I am the child! My PID: %d, Parent PID: %d\n", 
               getpid(), getppid());
        
        // Typically, child calls exec() here
        // execlp("ls", "ls", "-la", NULL);
        
        // Or child does its own work
        sleep(2);
        printf("Child exiting\n");
        _exit(0);  // Use _exit() in child after fork
    } else {
        // Parent process
        printf("I am the parent! My PID: %d, Child PID: %d\n", 
               getpid(), pid);
        
        // Parent continues
        int status;
        waitpid(pid, &status, 0);  // Wait for child
        printf("Child exited with status: %d\n", WEXITSTATUS(status));
    }
    
    return 0;
}

Fork vs Copy-on-Write

Modern systems use Copy-on-Write (COW) for efficiency:

/*
 * Copy-on-Write Fork Behavior
 * 
 * Instead of immediately copying all memory pages,
 * the parent and child share the same pages.
 * Pages are only copied when either process
 * tries to modify them.
 */

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    COPY-ON-WRITE FORK                           โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                                  โ”‚
โ”‚   BEFORE WRITE:                                                  โ”‚
โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”      โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                          โ”‚
โ”‚   โ”‚ Parent Page โ”‚  =   โ”‚ Child Page  โ”‚  (Same physical page,   โ”‚
โ”‚   โ”‚ 0x1000      โ”‚      โ”‚ 0x2000      โ”‚   read-only for both)   โ”‚
โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜      โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                          โ”‚
โ”‚                                                                  โ”‚
โ”‚   Child writes to address 0x2000:                               โ”‚
โ”‚                                                                  โ”‚
โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”      โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                          โ”‚
โ”‚   โ”‚ Parent Page โ”‚      โ”‚ Child Page  โ”‚  (New copy created      โ”‚
โ”‚   โ”‚ 0x1000      โ”‚      โ”‚ 0x3000      โ”‚   for child only)       โ”‚
โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜      โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                          โ”‚
โ”‚                                                                  โ”‚
โ”‚   Benefit: fork() is nearly instant, memory is copied lazily    โ”‚
โ”‚                                                                  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

The exec() Family

After fork(), the child typically calls exec() to replace its memory space with a new program.

// exec() family examples
#include <unistd.h>

int main() {
    pid_t pid = fork();
    
    if (pid == 0) {
        // Child - replace with new program
        
        // Option 1: execl() - list arguments
        execlp("ls", "ls", "-la", NULL);
        
        // Option 2: execv() - array of arguments
        char *args[] = {"ls", "-la", NULL};
        execvp("ls", args);
        
        // Option 3: execle() - with environment
        char *envp[] = {"PATH=/usr/bin", NULL};
        execle("/bin/ls", "ls", "-la", NULL, envp);
        
        // Option 4: execve() - with environment array
        char *args[] = {"ls", "-la", NULL};
        char *envp[] = {"PATH=/usr/bin", NULL};
        execve("/bin/ls", args, envp);
        
        // If we get here, exec failed
        perror("exec failed");
        _exit(1);
    } else {
        // Parent continues
        wait(NULL);
    }
    
    return 0;
}

Complete Fork-Exec Example

#!/usr/bin/env python3
"""Python implementation of fork-exec pattern."""

import os
import sys

def fork_exec_example():
    """Demonstrate fork-exec in Python."""
    
    print(f"Parent PID: {os.getpid()}")
    
    # Fork
    pid = os.fork()
    
    if pid < 0:
        print("Fork failed!")
        sys.exit(1)
    
    elif pid == 0:
        # Child process
        print(f"Child: My PID is {os.getpid()}, Parent PID is {os.getppid()}")
        
        # In a real program, you'd call os.execv() here
        # os.execv("/bin/ls", ["ls", "-la"])
        
        # For demo, just sleep
        import time
        time.sleep(1)
        print("Child exiting")
        os._exit(0)  # Important: use _exit in child
    
    else:
        # Parent process
        print(f"Parent: Created child with PID {pid}")
        
        # Wait for child
        status = os.waitpid(pid, 0)
        print(f"Parent: Child finished with status {status}")

if __name__ == "__main__":
    fork_exec_example()

Process Termination

Exit States

// Process termination flow
/*
 * โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
 * โ”‚         Process Calls exit()            โ”‚
 * โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
 *                    โ”‚
 *                    โ–ผ
 * โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
 * โ”‚  1. Close all open file descriptors    โ”‚
 * โ”‚  2. Flush output buffers               โ”‚
 * โ”‚  3. Release memory (via MMU)           โ”‚
 * โ”‚  4. Send SIGCHLD to parent             โ”‚
 * โ”‚  5. Change state to ZOMBIE            โ”‚
 * โ”‚  6. Store exit status in PCB          โ”‚
 * โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
 *                    โ”‚
 *                    โ–ผ
 * โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
 * โ”‚      PARENT CALLS wait() / waitpid()   โ”‚
 * โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
 *                    โ”‚
 *                    โ–ผ
 * โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
 * โ”‚  1. Parent reads exit status           โ”‚
 * โ”‚  2. Parent releases child PCB          โ”‚
 * โ”‚  3. Process is fully removed           โ”‚
 * โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main() {
    int status;
    
    // Different ways to exit
    exit(0);    // Normal exit with status 0
    _exit(0);   // Direct kernel exit (no flush)
    abort();    // Exit with SIGABRT
    
    // Wait for any child
    pid_t pid = wait(&status);
    
    // Check how child exited
    if (WIFEXITED(status)) {
        printf("Child exited normally with code %d\n", 
               WEXITSTATUS(status));
    }
    
    if (WIFSIGNALED(status)) {
        printf("Child killed by signal %d\n", WTERMSIG(status));
    }
    
    if (WIFSTOPPED(status)) {
        printf("Child stopped by signal %d\n", WSTOPSIG(status));
    }
    
    return 0;
}

Zombie and Orphan Processes

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                 ZOMBIE VS ORPHAN PROCESSES                      โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                                  โ”‚
โ”‚   ZOMBIE PROCESS                                                โ”‚
โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚   โ”‚                                                          โ”‚   โ”‚
โ”‚   โ”‚    Parent                                                โ”‚   โ”‚
โ”‚   โ”‚    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”                                              โ”‚   โ”‚
โ”‚   โ”‚    โ”‚ PID1 โ”‚ โ—„โ”€โ”€ waitpid() NOT called                    โ”‚   โ”‚
โ”‚   โ”‚    โ””โ”€โ”€โ”ฌโ”€โ”€โ”€โ”˜                                              โ”‚   โ”‚
โ”‚   โ”‚       โ”‚                                                  โ”‚   โ”‚
โ”‚   โ”‚       โ”‚ creates                                          โ”‚   โ”‚
โ”‚   โ”‚       โ–ผ                                                  โ”‚   โ”‚
โ”‚   โ”‚    Child (ZOMBIE)                                        โ”‚   โ”‚
โ”‚   โ”‚    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”                                              โ”‚   โ”‚
โ”‚   โ”‚    โ”‚ PID2 โ”‚ โ”€โ”€โ–บ Exited but PCB still exists             โ”‚   โ”‚
โ”‚   โ”‚    โ”‚ ZOMB โ”‚ โ”€โ”€โ–บ Waiting for parent to collect status    โ”‚   โ”‚
โ”‚   โ”‚    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                                              โ”‚   โ”‚
โ”‚   โ”‚                                                          โ”‚   โ”‚
โ”‚   โ”‚   Problem: Resource leak if parent never calls wait()   โ”‚   โ”‚
โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ”‚                                                                  โ”‚
โ”‚   ORPHAN PROCESS                                                โ”‚
โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚   โ”‚                                                          โ”‚   โ”‚
โ”‚   โ”‚    Parent (exits)                                        โ”‚   โ”‚
โ”‚   โ”‚    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”                                              โ”‚   โ”‚
โ”‚   โ”‚    โ”‚ PID1 โ”‚ โœ—                                             โ”‚   โ”‚
โ”‚   โ”‚    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                                              โ”‚   โ”‚
โ”‚   โ”‚       โ”‚                                                  โ”‚   โ”‚
โ”‚   โ”‚       โ”‚ (child continues)                                โ”‚   โ”‚
โ”‚   โ”‚       โ–ผ                                                  โ”‚   โ”‚
โ”‚   โ”‚    Child                                                 โ”‚   โ”‚
โ”‚   โ”‚    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”                                              โ”‚   โ”‚
โ”‚   โ”‚    โ”‚ PID2 โ”‚ โ”€โ”€โ–บ Adopted by init (PID 1)                โ”‚   โ”‚
โ”‚   โ”‚    โ”‚ RUN  โ”‚ โ”€โ”€โ–บ init waits for orphan children          โ”‚   โ”‚
โ”‚   โ”‚    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                                              โ”‚   โ”‚
โ”‚   โ”‚                                                          โ”‚   โ”‚
โ”‚   โ”‚   Solution: Child is adopted by init process            โ”‚   โ”‚
โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ”‚                                                                  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Handling Zombies

#!/usr/bin/env python3
"""Demonstrate zombie and orphan process handling."""

import os
import signal
import time
import sys

def demonstrate_zombie():
    """Create a zombie process."""
    print("Creating zombie process...")
    
    pid = os.fork()
    
    if pid == 0:
        # Child exits immediately - becomes zombie
        print(f"Child (PID: {os.getpid()}) exiting")
        os._exit(0)
    
    else:
        # Parent sleeps, not calling wait()
        # This makes child a zombie
        print(f"Parent (PID: {os.getpid()}) sleeping...")
        print(f"Child zombie PID: {pid}")
        time.sleep(5)
        
        # Now reap the zombie
        print("Parent calling wait()...")
        pid, status = os.waitpid(pid, 0)
        print(f"Reaped zombie PID: {pid}, status: {status}")

def demonstrate_orphan():
    """Create an orphan process."""
    print("Creating orphan process...")
    
    pid = os.fork()
    
    if pid == 0:
        # Child continues running
        print(f"Child (PID: {os.getpid()}) continuing")
        print(f"My parent is: {os.getppid()}")
        
        # Sleep to let parent exit
        time.sleep(2)
        
        # Now parent has exited, child is orphan
        # It should be adopted by init
        print(f"After parent exit, my parent is: {os.getppid()}")
        print("I am now an orphan adopted by init!")
        
        os._exit(0)
    
    else:
        # Parent exits immediately
        print(f"Parent (PID: {os.getpid()}) exiting immediately")
        print(f"Child PID: {pid}")
        # Don't wait - let child become orphan
        # Parent could also call os._exit(0) to speed this up

def demonstrate_double_fork():
    """
    Double-fork technique to prevent zombies.
    Classic pattern in daemon processes.
    """
    print("Demonstrating double-fork to prevent zombies...")
    
    pid = os.fork()
    
    if pid == 0:
        # First child
        pid2 = os.fork()
        
        if pid2 == 0:
            # Grandchild - the actual daemon
            # This process will be adopted by init
            print(f"Grandchild (PID: {os.getpid()}) running")
            # Do actual work here
            time.sleep(2)
            print("Grandchild exiting")
            os._exit(0)
        
        else:
            # First child exits immediately
            # Grandchild becomes orphan, adopted by init
            print(f"First child (PID: {os.getpid()}) exiting")
            os._exit(0)
    
    else:
        # Parent waits for first child
        os.waitpid(pid, 0)
        print("Parent reaped first child")
        print("Grandchild is orphan adopted by init - no zombie!")

if __name__ == "__main__":
    # Demonstrate each pattern
    # Uncomment to test:
    
    # demonstrate_zombie()
    # demonstrate_orphan()
    demonstrate_double_fork()

Process Scheduling

Scheduling Algorithms

"""Process scheduling algorithm implementations."""

from dataclasses import dataclass
from typing import List, Optional
from collections import deque

@dataclass
class Process:
    pid: int
    name: str
    arrival_time: int
    burst_time: int
    priority: int = 0
    
    remaining_time: int = 0
    
    def __post_init__(self):
        self.remaining_time = self.burst_time


class FCFSScheduler:
    """First-Come-First-Served (FIFO) Scheduler."""
    
    def schedule(self, processes: List[Process]) -> dict:
        """Execute FCFS scheduling."""
        current_time = 0
        results = []
        
        # Sort by arrival time
        ready_queue = sorted(processes, key=lambda p: p.arrival_time)
        
        for process in ready_queue:
            # Wait for process to arrive
            if current_time < process.arrival_time:
                current_time = process.arrival_time
            
            # Execute process
            start_time = current_time
            current_time += process.burst_time
            finish_time = current_time
            
            turnaround_time = finish_time - process.arrival_time
            waiting_time = turnaround_time - process.burst_time
            
            results.append({
                'pid': process.pid,
                'name': process.name,
                'start': start_time,
                'finish': finish_time,
                'turnaround': turnaround_time,
                'waiting': waiting_time
            })
        
        return results


class SJFScheduler:
    """Shortest Job First (Non-preemptive) Scheduler."""
    
    def schedule(self, processes: List[Process]) -> dict:
        """Execute SJF scheduling."""
        current_time = 0
        results = []
        completed = 0
        remaining = processes.copy()
        
        while completed < len(processes):
            # Find shortest job among arrived processes
            arrived = [p for p in remaining if p.arrival_time <= current_time]
            
            if not arrived:
                # Jump to next arrival
                current_time = min(p.arrival_time for p in remaining)
                continue
            
            # Select shortest burst time
            shortest = min(arrived, key=lambda p: p.burst_time)
            remaining.remove(shortest)
            
            # Execute
            start_time = current_time
            current_time += shortest.burst_time
            finish_time = current_time
            
            turnaround_time = finish_time - shortest.arrival_time
            waiting_time = turnaround_time - shortest.burst_time
            
            results.append({
                'pid': shortest.pid,
                'name': shortest.name,
                'start': start_time,
                'finish': finish_time,
                'turnaround': turnaround_time,
                'waiting': waiting_time
            })
            
            completed += 1
        
        return results


class RoundRobinScheduler:
    """Round Robin Time-Slice Scheduler."""
    
    def __init__(self, quantum: int = 4):
        self.quantum = quantum
    
    def schedule(self, processes: List[Process]) -> dict:
        """Execute Round Robin scheduling."""
        current_time = 0
        results = []
        ready_queue = deque()
        remaining = {p.pid: p for p in processes}
        
        # Add processes that have arrived
        for process in sorted(processes, key=lambda p: p.arrival_time):
            ready_queue.append(process)
        
        while ready_queue:
            process = ready_queue.popleft()
            
            # Execute time slice
            execute_time = min(self.quantum, process.remaining_time)
            current_time += execute_time
            process.remaining_time -= execute_time
            
            # Add newly arrived processes
            for p in list(remaining.values()):
                if p.arrival_time <= current_time and p not in ready_queue:
                    ready_queue.append(p)
            
            if process.remaining_time > 0:
                # Process not done, re-queue
                ready_queue.append(process)
            else:
                # Process complete
                results.append({
                    'pid': process.pid,
                    'name': process.name,
                    'finish': current_time,
                    'turnaround': current_time - process.arrival_time,
                    'waiting': current_time - process.arrival_time - process.burst_time
                })
                del remaining[process.pid]
        
        return results


# Example usage
if __name__ == "__main__":
    processes = [
        Process(1, "P1", 0, 10, 2),
        Process(2, "P2", 1, 5, 1),
        Process(3, "P3", 2, 8, 3),
        Process(4, "P4", 3, 6, 2),
    ]
    
    print("=== FCFS Scheduling ===")
    fcfs = FCFSScheduler()
    for r in fcfs.schedule(processes):
        print(f"{r['name']}: Turnaround={r['turnaround']}, Waiting={r['waiting']}")
    
    print("\n=== SJF Scheduling ===")
    sjf = SJFScheduler()
    for r in sjf.schedule(processes):
        print(f"{r['name']}: Turnaround={r['turnaround']}, Waiting={r['waiting']}")
    
    print("\n=== Round Robin (quantum=4) ===")
    rr = RoundRobinScheduler(quantum=4)
    for r in rr.schedule(processes):
        print(f"{r['name']}: Turnaround={r['turnaround']}, Waiting={r['waiting']}")

Inter-Process Communication

IPC Mechanisms

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚              INTER-PROCESS COMMUNICATION                         โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                                  โ”‚
โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚   โ”‚ SHARED MEMORY                                           โ”‚   โ”‚
โ”‚   โ”‚ โ€ข Fastest - kernel doesn't mediate                     โ”‚   โ”‚
โ”‚   โ”‚ โ€ข Requires synchronization (semaphores/mutex)         โ”‚   โ”‚
โ”‚   โ”‚ โ€ข Use: High-performance data sharing                    โ”‚   โ”‚
โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ”‚                                                                  โ”‚
โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚   โ”‚ MESSAGE PASSING                                         โ”‚   โ”‚
โ”‚   โ”‚ โ€ข Pipes, Message Queues                                โ”‚   โ”‚
โ”‚   โ”‚ โ€ข Kernel-mediated, buffered                           โ”‚   โ”‚
โ”‚   โ”‚ โ€ข Use: Simple communication                            โ”‚   โ”‚
โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ”‚                                                                  โ”‚
โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚   โ”‚ SIGNALS                                                 โ”‚   โ”‚
โ”‚   โ”‚ โ€ข Async notifications                                  โ”‚   โ”‚
โ”‚   โ”‚ โ€ข Software interrupts                                  โ”‚   โ”‚
โ”‚   โ”‚ โ€ข Use: Process control, errors                        โ”‚   โ”‚
โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ”‚                                                                  โ”‚
โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚   โ”‚ SEMAPHORES                                              โ”‚   โ”‚
โ”‚   โ”‚ โ€ข Counting/waiting primitives                         โ”‚   โ”‚
โ”‚   โ”‚ โ€ข Used for synchronization                             โ”‚   โ”‚
โ”‚   โ”‚ โ€ข Use: Producer-consumer, critical sections          โ”‚   โ”‚
โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ”‚                                                                  โ”‚
โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚   โ”‚ SOCKETS                                                 โ”‚   โ”‚
โ”‚   โ”‚ โ€ข Network communication                                โ”‚   โ”‚
โ”‚   โ”‚ โ€ข Can also be local (Unix domain)                     โ”‚   โ”‚
โ”‚   โ”‚ โ€ข Use: Network services, distributed systems          โ”‚   โ”‚
โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ”‚                                                                  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Pipe Example

// Parent-child communication via pipe
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>

int main() {
    int pipefd[2];
    char write_msg[] = "Hello from parent!";
    char read_msg[100];
    
    // Create pipe
    if (pipe(pipefd) == -1) {
        perror("pipe failed");
        return 1;
    }
    
    pid_t pid = fork();
    
    if (pid < 0) {
        perror("fork failed");
        return 1;
    }
    
    if (pid == 0) {
        // Child process
        close(pipefd[1]);  // Close write end
        
        // Read from pipe
        read(pipefd[0], read_msg, sizeof(read_msg));
        printf("Child received: %s\n", read_msg);
        
        close(pipefd[0]);
        _exit(0);
    
    } else {
        // Parent process
        close(pipefd[0]);  // Close read end
        
        // Write to pipe
        write(pipefd[1], write_msg, strlen(write_msg) + 1);
        
        close(pipefd[1]);
        wait(NULL);  // Wait for child
    }
    
    return 0;
}

Shared Memory Example

// Shared memory communication
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/shm.h>
#include <sys/wait.h>

#define SHM_SIZE 1024

int main() {
    int shm_id;
    char *shm_ptr;
    
    // Create shared memory segment
    shm_id = shmget(IPC_PRIVATE, SHM_SIZE, IPC_CREAT | 0666);
    if (shm_id < 0) {
        perror("shmget failed");
        return 1;
    }
    
    pid_t pid = fork();
    
    if (pid < 0) {
        perror("fork failed");
        return 1;
    }
    
    if (pid == 0) {
        // Child - attach and write
        shm_ptr = (char *)shmat(shm_id, NULL, 0);
        
        sprintf(shm_ptr, "Message from child process!");
        
        shmdt(shm_ptr);
        _exit(0);
    
    } else {
        // Parent - attach and read
        wait(NULL);
        
        shm_ptr = (char *)shmat(shm_id, NULL, SHM_RDONLY);
        
        printf("Parent received: %s\n", shm_ptr);
        
        shmdt(shm_ptr);
        
        // Remove shared memory
        shmctl(shm_id, IPC_RMID, NULL);
    }
    
    return 0;
}

Process Monitoring Commands

Linux Process Commands

# View all processes
ps aux                    # All processes with details
ps -ef                    # Full format listing
ps -ejH                   # Process tree

# Real-time process monitoring
top                       # Interactive process viewer
htop                      # Enhanced top (color, mouse)
btop                      # Modern, GPU-accelerated

# Process tree
pstree                    # Tree view of processes
pstree -p                # Show PIDs

# Find processes
pgrep -a pattern         # Find by name
pidof process_name       # Get PID by name
fuser -t port            # Process using port

# Process details
/proc/[pid]/status       # Process status
/proc/[pid]/maps         # Memory mappings
/proc/[pid]/fd           # Open file descriptors
/proc/[pid]/stack        # Stack trace

# Control processes
kill -l                  # List signals
kill -TERM pid           # Send SIGTERM
kill -KILL pid          # Send SIGKILL
pkill -9 pattern        # Kill by pattern

# Process resources
lsof -p pid              # Files opened by process
strace -p pid            # Trace system calls
ltrace -p pid            # Trace library calls

/proc Filesystem

#!/usr/bin/env python3
"""Read process information from /proc."""

import os
import time

def get_process_info(pid: int) -> dict:
    """Read process information from /proc."""
    proc_path = f"/proc/{pid}"
    
    try:
        # Read status file
        with open(f"{proc_path}/status") as f:
            status = {}
            for line in f:
                if ':' in line:
                    key, value = line.split(':', 1)
                    status[key] = value.strip()
        
        # Read cmdline
        with open(f"{proc_path}/cmdline") as f:
            cmdline = f.read().replace('\x00', ' ').strip()
        
        return {
            'pid': pid,
            'name': status.get('Name', 'unknown'),
            'state': status.get('State', 'unknown'),
            'cmdline': cmdline,
            'ppid': int(status.get('PPid', 0)),
            'vmrss': status.get('VmRSS', '0 kB'),  # Resident memory
            'vmdata': status.get('VmData', '0 kB'),  # Data segment
        }
    
    except (FileNotFoundError, ProcessLookupError):
        return None


def list_all_processes():
    """List all running processes."""
    processes = []
    
    for pid_str in os.listdir('/proc'):
        if pid_str.isdigit():
            info = get_process_info(int(pid_str))
            if info:
                processes.append(info)
    
    return processes


def find_process_by_name(name: str):
    """Find processes by name."""
    matches = []
    
    for proc in list_all_processes():
        if name.lower() in proc['name'].lower():
            matches.append(proc)
    
    return matches


if __name__ == "__main__":
    # List my own processes
    my_pid = os.getpid()
    info = get_process_info(my_pid)
    
    print(f"Current process info:")
    for key, value in info.items():
        print(f"  {key}: {value}")
    
    # Find python processes
    print("\nPython processes:")
    for proc in find_process_by_name('python'):
        print(f"  PID {proc['pid']}: {proc['name']} - {proc['state']}")

Conclusion

Process management is at the heart of every operating system. From the moment a program starts to when it terminates, the OS carefully orchestrates its lifecycle through creation, scheduling, execution, and cleanup.

Key takeaways:

  1. Processes are more than programs - They include the runtime environment, resources, and state maintained by the OS
  2. Fork-exec is fundamental - This Unix pattern for process creation enables process isolation and program replacement
  3. Process states matter - Understanding READY, RUNNING, BLOCKED, and ZOMBIE states helps debug issues
  4. Scheduling affects performance - Different algorithms have different characteristics; modern OSes use sophisticated multi-level approaches
  5. IPC enables collaboration - Processes can communicate through pipes, shared memory, signals, and sockets

In the next article, we’ll dive deeper into Process Scheduling Algorithms to understand how modern operating systems decide which process runs when.


External Resources

Books

Documentation

Comments