Process Management — Processes, PCB, States & Context Switch
1955 words
10 min read
Visual companion
Python
Type and operator map
Python Week 1: the first filter for runtime behavior
View
Revision summary
What this note is really saying
Short form
# Process Management — Processes, PCB, States & Context Switch > **Learning Objectives:** After this topic, you will be able to explain what a process is, describe its states and transitions, create processes using fork/exec, and trace context switch behavior. ## 🎯 Learning Objectives - Define a process and differe...

Process Management — Processes, PCB, States & Context Switch
Learning Objectives: After this topic, you will be able to explain what a process is, describe its states and transitions, create processes using fork/exec, and trace context switch behavior.
🎯 Learning Objectives
- Define a process and differentiate it from a program
- Identify all process states and valid transitions between them
- Describe the contents and purpose of a Process Control Block (PCB)
- Explain context switching and calculate its overhead
- Write programs using
fork(),exec(),wait(), andexit()system calls - Distinguish between zombie and orphan processes
📋 Prerequisites
- BSCS3031 (CSD): Basic understanding of CPU, memory, registers
- BSCS3005 (C Programming): System calls, process creation in C
1. What is a Process?
1.1 Intuition
A program is a passive entity — a file on disk containing instructions. A process is an active entity — the program in execution with all its associated resources (memory, CPU registers, open files). Think of a program as a recipe book and a process as the actual cooking happening in the kitchen.
1.2 Formal Definition
A process is an instance of a program in execution, characterized by:
- Text section: program code (instructions)
- Data section: global/static variables
- Heap: dynamically allocated memory
- Stack: function call frames, local variables
- Registers: Program Counter (PC), Stack Pointer (SP), CPU registers
- OS resources: open file descriptors, network connections, etc.
pseudo┌─────────────────────────────────┐ │ Process Memory │ ├─────────────────────────────────┤ │ Stack (LIFO) │ ← grows downward ├─────────────────────────────────┤ │ ↓ │ │ (free memory) │ │ ↑ │ ├─────────────────────────────────┤ │ Heap │ ← grows upward ├─────────────────────────────────┤ │ Data (BSS + initialized) │ ├─────────────────────────────────┤ │ Text (Program Code) │ └─────────────────────────────────┘
1.3 Program vs Process
| Aspect | Program | Process |
|---|---|---|
| Nature | Passive (file on disk) | Active (in memory + CPU) |
| Lifetime | Permanent (until deleted) | Temporary (from creation to termination) |
| Resources | Zero | CPU, memory, files, I/O |
| Identifier | Filename | PID (Process ID) |
| Multiple instances | One file | Many processes possible |
1.4 Process States
A process transitions through several states during its lifecycle:
(Diagram)
State descriptions:
| State | Description |
|---|---|
| New | Process is being created (PCB allocated, not yet ready) |
| Ready | Process is in main memory, waiting for CPU allocation |
| Running | Process is currently executing on the CPU |
| Waiting (Blocked) | Process is waiting for I/O or an event to complete |
| Terminated | Process has finished execution, PCB retained temporarily |
1.5 Process Control Block (PCB)
The PCB is a data structure in the kernel that stores all information about a process. Created when the process is created, it is the only persistent record of the process.
(Diagram)
PCB Fields:
| Field | Description |
|---|---|
| PID | Unique process identifier |
| Process State | Current state (new, ready, running, waiting, terminated) |
| Program Counter | Address of next instruction to execute |
| CPU Registers | Saved register values (for context switch) |
| Memory Limits | Base and limit registers, page tables |
| Open Files | List of file descriptors |
| CPU Scheduling Info | Priority, time quantum, scheduling class |
| Accounting Info | CPU time used, process ownership |
| I/O Status | List of I/O devices allocated to process |
2. Context Switching
2.1 Intuition
A context switch is the mechanism by which the OS pauses one process and resumes another on the CPU. The kernel saves the state (registers, PC, SP) of the current process into its PCB and loads the saved state of the next process. This happens tens to hundreds of times per second.
2.2 Context Switch Flow
(Diagram)
2.3 Context Switch Overhead
Context switching is pure overhead — the CPU does no useful work during the switch.
| Component | Time (typical) |
|---|---|
| Save registers (A) | ~0.5 - 2 μs |
| Scheduler decision | ~0.5 - 5 μs |
| TLB flush + cache miss | ~10 - 100 μs |
| Load registers (B) | ~0.5 - 2 μs |
| Total | ~11 - 109 μs |
Example: If a system does 1000 context switches/second with 50 μs each, the overhead is:
3. Process Creation
3.1 fork() System Call
The
fork() system call creates a new process (child) as an almost exact copy of the calling process (parent).
(Diagram)
Key properties of fork():- Child gets a copy of parent's address space (not shared — copy-on-write in modern OS)
- Both processes execute the next instruction after fork()
- Return value differs: 0 in child, child's PID in parent, -1 on error
- Child inherits: open files, signal handlers, environment, working directory
3.2 exec() Family
The
exec() system call replaces the current process image with a new program.| Function | Description |
|---|---|
execl() | List of arguments (null-terminated) |
execv() | Array of arguments |
execlp() | Like execl, but searches PATH |
execvp() | Like execv, but searches PATH |
3.3 wait() and exit()
wait(): Parent blocks until child terminates. Returns child's PID.waitpid(pid, &status, options): Wait for a specific child.exit(status): Terminates process, returns status to parent.
3.4 Zombie and Orphan Processes
(Diagram)
| Process Type | Description |
|---|---|
| Zombie | Child exited, parent hasn't called wait() yet. PCB retained (memory leak if parent never calls wait). |
| Orphan | Parent exited before child. Child adopted by init (PID 1), which automatically calls wait(). |
4. Worked Examples
Example 1: Simple fork()
c#include <stdio.h> #include <unistd.h> int main() { pid_t pid = fork(); if (pid == 0) { printf("CHILD: My PID = %d, Parent PID = %d\n", getpid(), getppid()); } else if (pid > 0) { printf("PARENT: My PID = %d, Child PID = %d\n", getpid(), pid); } else { perror("fork failed"); } return 0; }
Output (order may vary):
pseudoPARENT: My PID = 1000, Child PID = 1001 CHILD: My PID = 1001, Parent PID = 1000
Tracing:
| Step | Action | Processes |
|---|---|---|
| 1 | Program starts as single process | P(PID=1000) |
| 2 | fork() called | P(1000) + C(1001) created |
| 3a | Parent: pid > 0, prints message | P(1000) running |
| 3b | Child: pid == 0, prints message | C(1001) running |
| 4 | Both processes return 0 and exit | Both terminate |
Example 2: fork() with wait()
c#include <stdio.h> #include <unistd.h> #include <sys/wait.h> int main() { pid_t pid = fork(); if (pid == 0) { // Child process printf("Child starts work...\n"); sleep(2); // Simulate work printf("Child finishes work.\n"); return 42; // Exit with status } else { // Parent process int status; printf("Parent waiting for child...\n"); wait(&status); // Block until child exits printf("Child exited with status: %d\n", WEXITSTATUS(status)); } return 0; }
Tracing:
| Time | Parent State | Child State | Output |
|---|---|---|---|
| 0 | Running | -- | -- |
| 0.1 | fork() done | Ready | "Parent waiting..." |
| 0.2 | Blocked (wait) | Running | "Child starts work..." |
| 2.2 | Blocked (wait) | Running | -- |
| 2.3 | Blocked (wait) | Terminated | "Child finishes work." |
| 2.4 | Ready | -- | -- |
| 2.5 | Running | -- | "Child exited with status: 42" |
Example 3: Zombie Process
c#include <stdio.h> #include <unistd.h> #include <sys/wait.h> int main() { pid_t pid = fork(); if (pid == 0) { printf("Child (PID: %d) exiting now...\n", getpid()); _exit(0); // Child exits immediately } else { printf("Parent (PID: %d) sleeping without wait...\n", getpid()); sleep(30); // Parent sleeps — child becomes zombie printf("Parent awake, now calling wait()\n"); wait(NULL); printf("Child reaped, zombie gone.\n"); } return 0; }
During the 30-second sleep, run
ps -l in another terminal. You'll see the child process with status Z (zombie).5. Common Pitfalls
Pitfall 1: Assuming fork() creates a shared address space
Mistake: Students think child and parent share variables.
cint x = 10; fork(); x = 20; // Each process has its own x!
Why: After
fork(), each process has its own copy of memory (copy-on-write).
Correct: Use IPC (shared memory, pipes) for communication.Pitfall 2: Forgetting to handle all three fork() return values
Mistake: Only checking for 0 (child) and assuming success.
Correct: Always check for -1 (fork failure)!
cif (pid == -1) { perror("fork"); exit(1); } if (pid == 0) { /* child */ } else { /* parent */ }
Pitfall 3: Creating zombies by never calling wait()
Mistake: Parent exits without waiting, leaving zombies.
Solution: Always call
wait() or waitpid(), or register SIGCHLD handler.6. 📐 Key Formulas / Concepts
| Concept | Definition | Key Insight |
|---|---|---|
| Process | Program in execution | Active entity with PCB |
| Context switch | Saving/loading process state | Pure overhead (no useful work) |
fork() | Creates child process | Returns 0 (child) or PID (parent) |
exec() | Replaces process image | Never returns on success |
wait() | Parent blocks for child | Prevents zombies |
| Zombie | Process that exited but PCB retained | PCB memory leak |
| Orphan | Process whose parent exited | Adopted by init (PID 1) |
7. 📝 Practice Questions
Q1: What is the output of this code? (Assume fork succeeds)cint main() { fork(); printf("Hello\n"); fork(); printf("World\n"); return 0; }Answer: The string "Hello" prints 2 times, and "World" prints 4 times.Trace:
- P0 starts → fork() → P0(Hello) + P1(Hello)
- P0 fork() → P0(World) + P2(World)
- P1 fork() → P1(World) + P3(World)
Total: 2 "Hello", 4 "World" Q2: Name three differences between a program and a process.
| Aspect | Program | Process |
|---|---|---|
| Nature | Passive (disk) | Active (memory + CPU) |
| Lifetime | Permanent | Temporary |
| Resources | None | CPU, memory, files |
Q3: How does the OS save and restore the context of a process during a context switch?Answer: The OS saves the current register values (PC, SP, general-purpose registers) into the PCB of the current process. It then loads the saved registers from the PCB of the next process into the CPU. This involves:
- Trap to kernel mode (interrupt or system call)
- Save current process state to PCB
- Run scheduler to pick next process
- Load next process state from PCB
- Return to user mode at the saved PC Q4: What happens if a parent process terminates before its child?
Answer: The child becomes an orphan process. It gets adopted by theinitprocess (PID 1), which periodically callswait()to reap it. The orphan continues running normally but its parent PID becomes 1. Q5: Consider a system with context switch time of 20 μs and a time quantum of 10 ms. What percentage of CPU time is spent on context switching?Answer:
- Each context switch: 20 μs = 0.02 ms
- Per time quantum: 2 context switches (one out, one in) = 0.04 ms
- Overhead per quantum: 0.04 / (10 + 0.04) ≈ 0.4%
- CPU utilization ≈ 99.6% Q6: Explain the difference between a zombie process and an orphan process.
Answer:
- Zombie: Child has exited but parent hasn't called
wait()yet. PCB is still allocated.- Orphan: Parent has exited but child is still running. Child is adopted by init (PID 1), which calls
wait()automatically, so no zombie persists. Q7: What is the Process Control Block (PCB)? List at least 5 fields.Answer: The PCB is a kernel data structure storing all information about a process. Fields: PID, Process State, Program Counter, CPU Registers, Memory Limits, Open Files, CPU Scheduling Info, Accounting Info, I/O Status. Q8: Draw the 5-state process model and list all valid transitions.Answer: States: New → Ready → Running → Terminated, plus Ready → Running (dispatch), Running → Ready (preemption), Running → Waiting (I/O), Waiting → Ready (I/O complete). Q9: Write code using fork() where the child prints "Child" and the parent prints "Parent". Ensure the parent waits for the child.c#include <stdio.h> #include <unistd.h> #include <sys/wait.h> int main() { pid_t pid = fork(); if (pid == 0) { printf("Child\n"); } else if (pid > 0) { wait(NULL); printf("Parent\n"); } return 0; }Q10: What is the difference between exit() and _exit()?Answer:exit()performs cleanup (flush buffers, call atexit handlers, close streams) then calls_exit()._exit()immediately terminates the process. In child processes,_exit()is preferred to avoid flushing parent buffers.
8. 🔗 Cross-References
- Week 2 - Threads: Compare processes vs threads (lighter weight)
- Week 3 - CPU Scheduling: How the scheduler selects the next process
- Week 5 - Synchronization: IPC between processes
- BSCS3031 (CSD): Interrupt handling, system timer
- BSCS3005 (C Programming): System calls in C Join Discord NextOS Structures & Boot