skip to content

Threads and Processes

The units the OS actually schedules: processes with isolated address spaces versus threads sharing one, the thread lifecycle, what a context switch costs, and the 1:1, N:1 and M:N threading models. Interviewers use this area to test whether you can justify a thread pool over a thread per task.

part ofComputer science fundamentalsoverview, primer and where to startread it →
on this pageshow

questions

24

What is the difference between a process and a thread, and what does it actually mean to say that each process has its own address space?

level: juniorimportance: must knowfreq 85%

answer

  1. process = resource ownership; thread = scheduling
  2. page tables + MMU enforce isolation
  3. threads share heap/handles, not stack/registers
  4. pointer is meaningless outside its process
  5. crash blast radius: one process vs all threads

basics

~20 s

A process owns an isolated address space and OS resources; threads are execution contexts inside one process that share that memory. Each thread has its own stack, registers and program counter. Separate processes cannot touch each other's memory and must use explicit inter-process communication.

solid answer

~50 s

A **process** is the operating system's unit of *resource ownership*: it owns a virtual address space, open file handles, credentials and accounting state. A **thread** is the unit of *scheduling*: a program counter, register set and stack that a CPU can run. One process can hold many threads. Those threads share heap, globals and code, so handing data between them costs a pointer. Each thread still keeps its own stack and registers, because that is its private execution position. "Own address space" is a hardware fact, not a convention. The CPU's memory-management unit translates virtual addresses through per-process page tables, so address 0x4000 in process A and in process B refer to different physical memory, and touching an unmapped address faults instead of reading a neighbour's data. That isolation buys fault containment and a security boundary. It costs cheap sharing: cross-process communication must go through pipes, sockets, signals or explicitly mapped shared memory, and usually involves copying.

code

text · 5 lines
text
Process A:  addr 0x4000 -> page table A -> physical frame 17
Process B:  addr 0x4000 -> page table B -> physical frame 92

Thread 1 and Thread 2 inside Process A:
  both:     addr 0x4000 -> page table A -> physical frame 17   (same bytes)

go deeper

for a junior

Recall the core split: process owns memory and resources, thread is what gets scheduled, threads inside a process share the heap but not stacks.

for a middle

Explain the mechanism — page tables and the MMU — and derive the consequences: sharing needs IPC, crashes are contained, switching costs differ.

for a senior

Frame it as a design boundary: what blast radius and trust boundary you get for what communication cost, and where hybrid designs sit.

for a principal

Discuss isolation as an architectural primitive — fault domains, privilege reduction, per-tenant separation — and the throughput price of copying versus the operability gain.

## Two different units Operating systems separate two ideas that are easy to merge. - A **process** is the unit of **resource ownership**. It owns a virtual address space, a table of open file and socket handles, a working directory, user credentials, resource limits and accounting information. - A **thread** is the unit of **scheduling**. It is a program counter, a register set, a stack and a small amount of thread-private storage. The scheduler picks threads, not processes, when it decides what runs on a core. A process always contains at least one thread. A single-threaded process is simply a process whose one thread does all the work. ## What threads share and what they do not Threads inside a process share: the code, the heap, global and static data, open file handles, and the memory mapping itself. They do **not** share: the call stack, the register state (including the program counter), and any thread-local storage slots. That asymmetry explains the classic bug pattern. Two threads reading and writing the same heap object need synchronization, because they truly touch the same bytes. Two threads each using their own local variables never conflict, because those live on separate stacks. ## What address-space isolation really is Each process has its own **page tables**: a mapping from virtual addresses (the numbers the program uses) to physical frames of RAM. The memory-management unit consults those tables on every access. A page not mapped for the current process cannot be reached at all — the hardware raises a fault and the OS typically kills the offender. The consequences are worth stating plainly: 1. **A pointer is only meaningful inside its own process.** Sending the numeric value of a pointer to another process is useless; the same number means something else there. 2. **Isolation is enforced, not agreed.** A buggy or malicious process cannot scribble on another's data structures the way a buggy thread can scribble on its siblings'. 3. **Sharing must be requested.** Two processes only share memory when both explicitly map the same object (shared-memory segments or memory-mapped files). Otherwise every byte exchanged is copied through the kernel. ## What that trade buys **Fault containment.** A crash — a bad pointer, an out-of-memory kill, a stack overflow — takes down one process. Its siblings keep running, and a supervisor can restart it. In a multi-threaded process the same fault usually ends the entire process, taking every thread with it. **A security boundary.** Processes carry credentials and can be sandboxed, given reduced privileges, or confined so that even total compromise of one yields little. Threads inside a process all run with the same authority; there is no meaningful privilege boundary between them. **Freedom from shared-mutable-state hazards.** Two processes that communicate only by messages cannot race on a shared object, because there is no shared object. The hazards move elsewhere (protocol design, message ordering, partial failure), but the classic data race disappears by construction. ## What it costs **Creation and footprint.** A process needs its own page tables, handle table and kernel bookkeeping; a thread needs a stack and a small control block. Processes are heavier by an order of magnitude or more. **Communication.** Sending a megabyte between threads is a pointer assignment. Between processes it is a copy — often two, into and out of a kernel buffer — plus serialization if the data is not a flat byte blob. **Switching.** Switching between threads of the same process keeps the address space; switching between processes changes page tables and can invalidate address-translation caches, so it is more expensive. ## How to choose Use threads when the units of work must share large mutable state cheaply and trust each other. Use processes when you need a failure or trust boundary: untrusted code, plugins, per-tenant isolation, or a component whose crash must not take the whole system down. Many mature systems do both — a small number of isolated processes, each internally multi-threaded.

  • If threads share the heap, why does each thread still need its own stack?
    The stack records where a thread currently is: its call chain, return addresses, and local variables. Two threads execute different call chains at the same moment, so a shared stack would be meaningless and instantly corrupted. Locals living on private stacks is also why they need no synchronization.
  • Can two processes ever share memory directly, and what changes when they do?
    Yes — both can map the same shared-memory object or file into their address spaces, after which reads and writes hit the same physical pages with no copying. Once they do, they are back in shared-mutable-state territory: they need cross-process synchronization and the isolation guarantee no longer covers that region.
  • Which is cheaper to switch between, two threads of one process or two processes, and why?
    Two threads of the same process, because the address space stays the same: the kernel swaps registers and stacks but keeps the page tables and the address-translation caches valid. A process switch changes page tables, which can invalidate translation caches and force expensive re-population.

A process is an apartment with its own locked door; threads are flatmates inside it. Flatmates share the fridge and can spoil each other's food; neighbours have to knock and hand things through the door.

saying these in an interview costs you the question

  • Saying threads have their own memory, or that each thread gets its own heap.
  • Claiming isolation is enforced by the language or runtime rather than by hardware page tables.
  • Believing one process can read another's variables by address without shared memory being set up.
  • Saying a thread crash only kills that thread — an unhandled fault normally kills the whole process.
  • Treating 'process' and 'program' as the same thing; one program can run as many processes.

context

open as a page

Explain the difference between preemptive and cooperative scheduling of concurrent tasks, and what each model demands from the code being scheduled.

level: juniorimportance: must knowfreq 68%

basics

~20 s

Under preemptive scheduling a timer interrupt lets the scheduler suspend a task at almost any instruction and run another. Under cooperative scheduling a task runs until it voluntarily yields. Preemption guarantees progress for everyone; cooperation is cheaper and predictable but one non-yielding task blocks all others.

open as a page

Concurrency runtimes map the threads a program creates onto the threads an operating-system kernel actually schedules. Explain the 1:1, N:1 and M:N mappings, and what each one costs you.

level: juniorimportance: must knowfreq 58%

basics

~20 s

Three mappings of program threads onto kernel-scheduled threads. 1:1 — each program thread is a kernel thread: real parallelism, but costly. N:1 — many on one: cheap, but no multicore and one blocking call stalls all. M:N — both benefits, at the price of a complex two-level scheduler.

open as a page

What does it mean to join a thread you started, and what problem does joining solve that simply starting the thread does not?

level: juniorimportance: must knowfreq 60%

basics

~20 s

Joining means one thread blocks until another finishes. Starting a thread only launches it; the starter keeps running and has no idea when the work is done or whether it succeeded. Join gives you completion timing and a safe point to read results.

open as a page

What actually happens when the operating system switches from running one thread to another, and where does the cost of that switch come from?

level: middleimportance: must knowfreq 58%

basics

~20 s

The kernel saves the running thread's registers and program counter, picks another runnable thread, and restores its state; a switch to a different process also swaps page tables. The direct cost is small. The larger, indirect cost is cold caches, branch predictors and address-translation entries when the new thread starts running.

open as a page

Walk through the states a thread passes through from creation to termination, and explain the difference between a thread blocked trying to acquire a lock and a thread waiting to be signalled.

level: middleimportance: must knowfreq 70%

basics

~20 s

New (created, not started) → runnable (ready or actually running) → a non-runnable state → terminated, which is final. Blocked means it wants a lock someone else holds. Waiting means it voluntarily parked and needs another thread to signal it; timed-waiting also wakes on a deadline.

open as a page

What is thread-local storage, what problem does it solve, and what does using it cost you?

level: middleimportance: must knowfreq 55%

basics

~20 s

A variable whose value is per-thread: every thread reading the same name gets its own copy, so no synchronization is needed. It solves sharing non-thread-safe or per-request state without locks. It costs you invisible coupling, lifetime bugs on reused threads, and memory that scales with thread count.

open as a page

Why do modern runtimes refuse to let one thread forcibly terminate another, and what mechanism is used instead to stop work that is already running?

level: seniorimportance: must knowfreq 55%

basics

~20 s

A forced kill stops a thread at an arbitrary instruction, so it can leave shared data half-updated and locks held forever — damage the rest of the process cannot detect or repair. The replacement is cooperative cancellation: request a stop, and let the target check at safe points and unwind cleanly.

open as a page

A request handler stores a correlation identifier in thread-local storage so log lines can be tagged with it. When the handler hands work to a background worker pool, that identifier disappears from the logs. Explain why, and describe the general fix.

level: seniorimportance: must knowfreq 50%

basics

~20 s

Thread-local values are keyed to the thread, and the background worker is a different thread with its own empty slot. The fix is to capture the context on the submitting thread at submission time, carry it with the task, and install it on the executing thread around the task — always removing it afterwards.

open as a page

A service stores per-request data such as the caller's identity in thread-local storage while handling requests on a pool of reusable worker threads. What can go wrong across requests, and how do you prevent it?

level: seniorimportance: must knowfreq 55%

basics

~20 s

Pool threads never die, so a value left in a thread-local slot survives into the next request on that worker. That leaks memory and, worse, leaks data: a later request that forgets to set the identity silently inherits the previous caller's. Always clear in a finally block.

open as a page

Explain the fork-then-exec model of creating a new process, including what copy-on-write does, and why creating a process is normally more expensive than creating a thread.

level: middleimportance: should knowfreq 50%

basics

~20 s

Fork clones the calling process, giving the child a duplicate address space and handle table; exec then replaces the child's program image with a new one. Copy-on-write makes the duplicate lazy — pages are shared read-only until written. Processes still cost more than threads: page tables, handle tables and kernel bookkeeping versus one stack.

open as a page

Why do preemptive schedulers give tasks a bounded time slice, and how do priorities interact with fairness when many tasks are runnable at once?

level: middleimportance: should knowfreq 42%

basics

~20 s

A time slice bounds how long one task can hold a core, trading throughput for responsiveness: long slices amortize switch cost, short slices cut waiting time for others. Priorities bias the choice of who runs next; strict priority can starve low-priority work, so schedulers add aging or proportional shares.

open as a page

A machine can comfortably run a few thousand operating-system threads, yet some runtimes host a million lightweight (runtime-scheduled) threads on the same hardware. What actually accounts for that difference, and where is the new ceiling?

level: middleimportance: should knowfreq 52%

basics

~20 s

An OS thread carries a large fixed stack reservation plus non-pageable kernel bookkeeping, and every switch is a trip through the kernel. A lightweight thread is a heap object with a tiny growable stack switched in user space — kilobytes, not megabytes. The new ceiling is heap and live stack depth.

open as a page

Many runtimes distinguish background (daemon) threads from ordinary user threads. What is the difference in how each affects process shutdown, and what risk do you accept when you mark a worker as a background thread?

level: middleimportance: should knowfreq 45%

basics

~20 s

The process stays alive while any ordinary (user) thread is still running; background (daemon) threads do not keep it alive and are abandoned mid-work when the last user thread ends. That makes them safe for infinite loops but unsafe for anything that must finish, like a buffered write.

open as a page

Compare the main inter-process communication mechanisms — pipes, shared memory, sockets and signals — and explain how you would choose among them for moving a high volume of data between two processes on the same machine.

level: seniorimportance: should knowfreq 45%

basics

~20 s

Pipes are simple one-way byte streams via kernel buffers. Sockets are the same idea but routable across machines. Shared memory is the only zero-copy option — mapped by both sides, needing its own synchronization. Signals carry notification, not data. For high volume on one host, use shared memory with a ring buffer, or pipes if simplicity wins.

open as a page

What happens to throughput and latency when a machine has far more runnable threads than CPU cores, and how would you recognize that state from the outside?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Beyond one runnable thread per core, throughput stops rising and eventually falls: extra threads add context switches, cache eviction and lock contention rather than work. Latency grows roughly with the queue of runnable threads. From outside you see high run-queue length, many involuntary context switches, high CPU with falling completion rate, and rising tail latency.

open as a page

In a runtime where lightweight threads are multiplexed over a small pool of host operating-system threads, certain operations "pin" a lightweight thread to its host so it cannot be unmounted. What causes pinning, how would you detect it in production, and what does it do to throughput?

level: seniorimportance: should knowfreq 30%

basics

~20 s

Pinning means a lightweight thread cannot be unmounted from its host OS thread — usually because it is blocked inside native code or in a construct whose state the runtime cannot relocate. The host is consumed while it waits, so effective parallelism shrinks; if all hosts pin, everything stops.

open as a page

Two-level (M:N) scheduling multiplexes many user-level threads over a smaller set of kernel-scheduled threads. What does it buy, and why did several mature operating systems abandon their M:N implementations in favour of one-to-one kernel threading?

level: seniorimportance: should knowfreq 34%

basics

~20 s

M:N buys cheap, numerous threads that still use every core. It is hard because a blocking system call removes a host kernel thread from service, and two schedulers must agree about priorities, signals, locks, and what debuggers and profilers see. Once kernel threads got cheap, the complexity stopped paying.

open as a page

What happens when an exception propagates out of the top of a thread's entry function, and why do teams so often fail to notice that it happened?

level: seniorimportance: should knowfreq 45%

basics

~20 s

The thread's stack unwinds, a per-thread failure hook (or a default that prints to standard error) runs, and the thread terminates. Nothing propagates to whoever started it, so the failure is invisible unless you installed a hook — the symptom is silently missing work.

open as a page

When ambient context is carried across a thread hand-off, you can either copy its value at hand-off time or have the receiving side read the current value when it eventually runs. Compare the two semantics and say when each is correct.

level: seniorimportance: should knowfreq 35%

basics

~20 s

Capturing takes a snapshot at hand-off, so the task sees the context of the operation that created it, however late it runs. Re-reading sees whatever is current on the executing thread — usually empty or another task's leftovers. Capture is almost always what you want; re-read only suits genuinely global, dynamic settings.

open as a page

You are designing a server that runs untrusted extension code and must stay available when one unit of work crashes. Make the case for a multi-process architecture versus a multi-threaded one, and state honestly what each choice costs.

level: principalimportance: should knowfreq 40%

basics

~20 s

Multi-process gives hardware-enforced fault and trust boundaries: a crash, leak or compromise is contained and the supervisor restarts one worker. It costs memory per worker, copying on every message, and slower startup. Multi-threaded is cheaper and shares state for free, but one bad pointer or hostile extension takes down everything.

open as a page

You can serve each request either on its own operating-system thread or on its own lightweight, runtime-scheduled thread over a small host pool. How do you decide, and which assumptions elsewhere in the system does the lightweight model invalidate?

level: principalimportance: should knowfreq 36%

basics

~20 s

Decide from in-flight concurrency and the fraction of time a request spends waiting, not from fashion. Lightweight threads win when tens of thousands of mostly-waiting requests must be represented. They invalidate pool-size-as-backpressure, thread-identity assumptions, OS priorities, per-thread buffers, and any blocking the runtime cannot mediate.

open as a page

Some designs carry per-operation values such as tenant or user identity as explicit parameters through the call chain; others keep them in ambient per-thread storage read implicitly by any layer. How would you decide between these approaches for a large service?

level: principalimportance: should knowfreq 35%

basics

~20 s

Split by consequence of being wrong. Values that change results or access — tenant, user, deadline — go in explicit parameters, so a missing value is a compile or call-site error. Values that only annotate — correlation ids, log tags — can be ambient, because losing them degrades diagnostics, not correctness.

open as a page

What are CPU affinity and per-core run queues, and when is deliberately pinning work to specific cores worth the loss of scheduler flexibility?

level: principalimportance: nice to knowfreq 28%

basics

~20 s

Schedulers keep per-core run queues and prefer to resume a thread on its previous core, because its cached data is there; idle cores steal work from busy queues. Explicit pinning fixes a thread to chosen cores, protecting cache and memory locality for latency-critical work, at the cost of the scheduler's ability to balance load.

open as a page