Post

The Anatomy of a Process

Hiring is a process… and so is firing.

None of those are the processes we’ll be discussing today. They’re probably better covered by HR professionals than a software engineer with a keyboard and questionable opinions about operating systems.

Instead, this is an account of my understanding of how a program evolves into a process and what happens across the lifetime of that process.

On to the details…

A sharp distinction exists between the concepts program and process. A program is a passive collection of instructions and data stored on disk. A process on the other hand is a running instance of that program together with the resources required to execute it, such as memory, CPU state, and open files.

Before any process comes to life, there is an executable file (the program) lying on disk with instructions of how the resulting process will execute. Within the executable file are metadata structures such as the ELF (Executable and Linkable Format) header and program header table. These describe the segments that should be mapped into memory, such as the code, data, and read-only data segments.

In Unix-like systems, a program is typically executed via a combination of fork() + execve() system calls. The parent process will clone itself via the fork call and produce a child process. The execve call forces the kernel to replace the child process’ program image with that of the target executable.

On an execve call, the kernel first locates the executable file on disk then parses the data from the program header table and uses that data to initialize memory descriptors (like the mm_struct in Linux) and Virtual Memory Areas (VMAs) that dictate the exact start and end addresses for each portion of the application.

What results from this operation is the creation of an address space: a collection of virtual memory mappings describing how the process will view memory. At this stage, many of the pages backing these mappings may not yet exist in physical memory and will be populated on demand through page faults, a concept called demand paging (I really haven’t gone deeper into this myself).

An address space is an abstracted view of system memory in which a process executes. Each process has its own virtual address space, providing strong isolation from other processes. You can think of it as a sandboxed memory environment where one process cannot directly observe or interfere with another.

When a process attempts to access a virtual address that is invalid or unmapped, the Memory Management Unit (MMU) triggers a page fault exception. This causes the CPU to transfer control to the kernel’s page fault handler. The kernel then determines whether the fault can be resolved (for example, by loading a missing page into memory) or whether the access is illegal.

If the access is deemed invalid, the kernel signals the offending process, typically resulting in termination with a segmentation fault.

The CPU’s memory management unit (MMU), configured by the operating system through page tables, translates virtual addresses generated by the process into physical memory addresses. To accelerate this translation, processors maintain a Translation LookAside Buffer (TLB), which caches recently used address mappings and avoids costly page table walks on subsequent accesses.

The OS will also do some other initialisation tasks, particularly as related to input/output (I/O). For example, in UNIX systems, each process by default has three open file descriptors, for standard input, output, and error.

Once the kernel initialises the process’ machine state (registers, program counter, stack and files), the process is in a ready state and becomes eligible to run. At some point, the scheduler selects it for execution, the CPU begins executing instructions from its entry point, and the program officially becomes a running process.

Process lifecycle diagram

I’m thinking of doing another article, since this one pretty much covers everything from a program to how we end up with a running process. Maybe I will talk about the anatomy of a system call and see how a running process requests the kernel for hardware-level resources.

Hope you found this useful, and perhaps learned something along the way.

This post is licensed under CC BY 4.0 by the author.