Operating Systems Introduction: Processes & Threads
What an operating system does, kernel vs user mode, system calls, and the difference between processes and threads.
What does an operating system do?
An operating system (OS) is the software that manages a computer's hardware and gives programs a safe, convenient way to use it. Linux, Windows, macOS, Android and iOS are all operating systems.
Its main jobs:
| Job | What it means | Example |
|---|---|---|
| Process management | Create, schedule and stop running programs | Running a browser, an IDE and Spotify at once |
| Memory management | Give each program its own memory and protect it from others | A crashing tab can't overwrite your editor's memory |
| File systems | Organise data on disks into files and folders | C:\Users\you\notes.txt, /home/you/notes.txt |
| Device I/O | Talk to keyboards, disks, network cards and GPUs through drivers | Reading a USB drive |
| Security & isolation | Users, permissions, sandboxing | You can't read another user's files |
| Networking | Sockets and protocol stacks | Your web server listening on port 80 |
As a backend developer you meet the OS every day: when a server runs out of file descriptors, a container is killed for using too much memory, or threads deadlock.
Kernel mode vs user mode
The kernel is the core of the OS. It runs in a privileged CPU mode (kernel mode) with full access to hardware. Your programs run in user mode, where they cannot touch hardware or other programs' memory directly.
┌──────────────── user mode ────────────────┐
│ browser python app database server │
└─────────────────────┬──────────────────────┘
│ system calls (open, read, write, fork, mmap…)
┌─────────────────────▼──────────────────────┐
│ kernel mode: scheduler, memory manager, │
│ file systems, drivers, network stack │
└─────────────────────┬──────────────────────┘
▼
hardware
This separation means a bug in your app crashes your app — not the whole machine.
System calls
A system call is how a user program asks the kernel to do something privileged. Even a simple print() ends up as a write system call.
| System call (Linux) | Purpose |
|---|---|
open, read, write, close |
File and socket I/O |
fork, exec, wait, exit |
Create and manage processes |
mmap, brk |
Request memory |
socket, bind, listen, accept |
Networking |
System calls are relatively expensive because the CPU switches between user and kernel mode. That's why buffered I/O (fewer, larger writes) is faster than many tiny writes. On Linux you can watch the system calls a program makes with strace.
Processes
A process is a running program. Each process has its own:
- Address space — code, data, heap and stack memory
- Process ID (PID)
- Open files, sockets and environment variables
- At least one thread of execution
A process moves through states:
new ──▶ ready ◀──────────── waiting (blocked on I/O)
│ ▲ ▲
scheduler │ │ time slice ends │ starts I/O
▼ │ │
running ────────────────┘
│
▼
terminated
Threads
A thread is a unit of execution inside a process. Threads in the same process share memory (heap, global variables, open files) but each has its own stack and registers.
| Process | Thread | |
|---|---|---|
| Memory | Separate address space | Shared with other threads in the process |
| Creation cost | Higher | Lower |
| Communication | IPC: pipes, sockets, shared memory | Directly via shared variables (needs locks) |
| Crash impact | Isolated | Can crash the whole process |
| Example | Each Chrome tab, each PHP-FPM worker | Threads in a Java web server handling requests |
You can see the difference in Python:
import threading
from multiprocessing import Process
counter = {"value": 0}
def increment():
counter["value"] += 1
if __name__ == "__main__":
t = threading.Thread(target=increment)
t.start(); t.join()
print("after thread: ", counter["value"]) # thread changed the shared dict
p = Process(target=increment)
p.start(); p.join()
print("after process:", counter["value"]) # child changed its own copy
Output:
after thread: 1
after process: 1
The thread updated the parent's memory. The child process incremented its own copy, so the parent still sees 1.
Context switching
The CPU core can run one thread at a time. The OS rapidly switches between threads — saving one thread's registers and loading another's. This context switch takes microseconds and gives the illusion that everything runs at once. Switching between processes is costlier than between threads because the memory mappings change too.
Concurrency vs parallelism
- Concurrency — dealing with many tasks at once by interleaving them (one core can do this).
- Parallelism — literally running tasks at the same instant on multiple cores.
A Node.js server is highly concurrent on a single thread; a video encoder using 8 cores is parallel.
Common mistakes
- Assuming threads are free — each has a stack (often 1–8 MB reserved) and adds context-switch cost.
- Sharing data between threads without synchronisation (see the Synchronization lesson).
- Forgetting that child processes don't share memory — use queues, pipes or a database.
- Confusing concurrency with parallelism.
Key takeaways
- The OS manages processes, memory, files, devices and security.
- User programs request privileged work through system calls into the kernel.
- Processes have separate memory; threads share their process's memory.
- Context switches let one core juggle many threads.