Welcome to the second module of our course. In the previous module, we focused on the internal logic of a program, analyzing data structures and algorithms to solve problems efficiently. Now, we shift our perspective outward to the environment where our programs run: the Operating System (OS). The OS is the foundational software that manages all hardware resources, and understanding its mechanisms is critical for building robust and performant applications.
In this first lesson of the OS module, we will explore the fundamental units of execution: processes and threads. Your work as an engineering manager often involves making architectural decisions that have profound implications for scalability, reliability, and performance. The choice between a multi-process and a multi-threaded architecture is one of the most basic, yet impactful, of these decisions. By the end of this lesson, you will be able to explain the difference between a process and a thread, including their distinct memory layouts and resource models.
From Program to Process
Let's start by clarifying the hierarchy. On your computer's storage, you have programs—static files containing executable instructions, like chrome.exe or a compiled Java application. When you run a program, the OS loads it into memory and begins its execution. This running instance of a program is called a process.
A process is more than just code; it's an active entity that the OS endows with a complete set of resources. To get a concise overview of this transformation, please watch the first part of the following video from ByteByteGo.
FANG Interview Question | Process vs Thread
This video provides a clear, animated explanation of what a program is and how it becomes a process with its own isolated resources.
Watch from the beginning until the end of the section on process isolation. Notice the emphasis on each process having its own memory address space—a crucial concept for stability.
As the video highlighted, the key characteristic of a process is isolation. The OS creates a private virtual address space for each process, ensuring that a bug or crash in one process (like a single browser tab) cannot affect others. This protected container holds everything the program needs to run.
To understand what's inside this container, let's examine the memory layout of a typical process.
Processes Vs Threads | Concurrency Interview | AlgoMaster.io
This article from AlgoMaster.io gives a precise breakdown of a process's structure and memory segments.
Please read the first two sections, Process Memory Layout. Pay close attention to the table describing the Text, Data, BSS, Heap, and Stack segments. This is the standard memory model for a process in modern operating systems.
To summarize what you've just read, a process is an isolated environment containing:
- Text (Code) Segment: The compiled machine code of the program. This is read-only.
- Data & BSS Segments: Global and static variables.
- Heap: Memory that is dynamically allocated during runtime (e.g., via
mallocin C ornewin Java/C++). It typically grows "upwards" towards higher memory addresses. - Stack: Memory for local variables, function parameters, and return addresses. Each function call pushes a "frame" onto the stack. It typically grows "downwards" towards lower memory addresses.
- Other Resources: File descriptors, network sockets, and security credentials, all managed by the OS.
Threads: Execution Within a Process
While processes provide isolation, they are also "heavyweight." Creating a new process requires the OS to allocate a full new memory space and resource set, which is a relatively slow operation. Many applications, such as web servers or databases, need to handle thousands of concurrent tasks. Creating a new process for each task would be inefficient.
This is where threads come in. A thread is a unit of execution within a process. A process starts with one thread (the "main thread") and can create others to perform tasks in parallel. Threads are often called "lightweight processes" because they share most of the parent process's resources.
The following image provides a clear visual distinction between the memory models.
This sharing is the defining feature of threads. Let's get a more detailed breakdown of what is shared and what is unique to each thread.
Processes Vs Threads | Concurrency Interview | AlgoMaster.io
This next section of the AlgoMaster.io article clearly defines a thread and lists its shared and private components.
Now, read the section What is a Thread?. The analogy of a house (process) and the people living in it (threads) is particularly effective.
As you've read, here is the essential breakdown:
Shared by all threads within a process:
- Code segment
- Data segment (global variables)
- Heap (dynamically allocated memory)
- File descriptors and other OS resources
Private to each thread:
- Stack: Each thread needs its own stack to manage its independent sequence of function calls.
- Registers: CPU registers, including the Program Counter (which instruction is next) and Stack Pointer, must be private to track the execution state of each thread.
- Thread ID: A unique identifier.
This architecture—shared memory with private execution context—is what makes threads both efficient and dangerous. Communication between threads is fast because they can simply read and write to the same memory locations (e.g., updating a shared object on the heap). However, this also means one misbehaving thread can corrupt data used by all other threads, potentially crashing the entire process.
The Architectural Trade-Off: Processes vs. Threads
As an engineering leader, understanding the trade-offs between these two models of concurrency is essential. The choice impacts performance, scalability, and reliability. The architecture of popular database systems provides a classic real-world example of this trade-off.
This article from PlanetScale uses the contrasting architectures of Postgres and MySQL to illustrate the practical implications of choosing processes over threads.
Please read the sections Postgres and MySQL and Threads. This will ground the abstract concepts in a concrete, business-relevant example.
The Postgres vs. MySQL example perfectly illustrates the core dilemma. Let's formalize these trade-offs.
-
Creation Cost & Resource Overhead: Creating a process is orders of magnitude slower than creating a thread. A process requires its own large virtual address space and significant kernel bookkeeping. A thread only needs a new stack and some registers. This is why a MySQL server (thread-per-connection) can typically handle more concurrent connections on the same hardware than a traditional Postgres server (process-per-connection).
-
Communication:
- Inter-Process Communication (IPC) is complex and slow. Since processes have separate memory spaces, they must rely on the OS to act as an intermediary, using mechanisms like pipes, sockets, or explicitly configured shared memory segments.
- Inter-Thread Communication is simple and fast. Threads can communicate directly through shared global variables and objects on the heap. This speed comes at the cost of requiring explicit synchronization mechanisms (like mutexes and semaphores, which we'll cover later) to prevent data corruption.
-
Fault Isolation:
- Processes provide strong fault isolation. If a process crashes, it does not affect any other processes. This is why Chrome runs each tab in a separate process—a crashing web page won't bring down your entire browser. This robustness is a key advantage of the Postgres model.
- Threads have no fault isolation. An unhandled error in one thread (e.g., a null pointer exception) will terminate the entire process, killing all other threads with it.
A Glimpse Under the Hood: PCB and TCB
To manage these entities, the operating system uses internal data structures: the Process Control Block (PCB) and the Thread Control Block (TCB). Understanding these structures reinforces the relationship between processes and threads.
- Process Control Block (PCB): Think of this as the OS's master record for a process. It contains the Process ID (PID), process state, pointers to the memory map (heap, stack, code), a list of open files, and security permissions. It holds all the process-wide information.
- Thread Control Block (TCB): This is a much smaller structure that stores only the execution context for a single thread: its Thread ID, state, program counter, CPU registers, and a pointer to its stack. Crucially, it also contains a pointer back to the parent process's PCB to access shared resources.
The OS scheduler manages TCBs, not PCBs directly. When the OS performs a context switch between two threads in the same process, it just has to save the registers of the old TCB and load the registers of the new one. If it switches between threads in different processes, it must do that plus the expensive step of switching the active memory map pointed to by the PCB.
Conclusion
In this lesson, we have dissected the fundamental units of OS execution. You can now confidently distinguish between a program, a process, and a thread.
Here are the key takeaways:
- A process is a running program with its own isolated memory space (code, data, heap, stack) and resources. It provides stability and security at the cost of high overhead.
- A thread is a unit of execution within a process. Threads share the process's memory (code, data, heap) but have their own private stack and registers. They enable efficient concurrency and communication at the cost of shared risk and no fault isolation.
- The choice between a multi-process and multi-threaded architecture is a fundamental design decision, balancing performance and efficiency (favoring threads) against robustness and isolation (favoring processes).
We've mentioned that switching between these tasks has a performance cost. In our next lesson, we will dive deeper into the mechanism of a context switch, examining exactly what the OS does to pause one task and start another, and analyzing the performance implications of this critical operation.