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:
- Processes are more than programs - They include the runtime environment, resources, and state maintained by the OS
- Fork-exec is fundamental - This Unix pattern for process creation enables process isolation and program replacement
- Process states matter - Understanding READY, RUNNING, BLOCKED, and ZOMBIE states helps debug issues
- Scheduling affects performance - Different algorithms have different characteristics; modern OSes use sophisticated multi-level approaches
- 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
- “Operating System Concepts” by Silberschatz, Galvin, Gagne
- “Modern Operating Systems” by Andrew Tanenbaum
- “Understanding the Linux Kernel” by Bovet & Cesati
Comments