Reading · Computer Systems

How a computer starts; processes and threads

The boot relay from firmware to your shell, where PID 1 fits in, what a process owns, how fork, copy-on-write and exec run every command you type, and what threads share.

11 min readUEFI/BIOS → bootloader → kernelsystemd as PID 1Process address spacefork, copy-on-write, execProcess vs thread

The practical sessions start at the shell prompt. This reading fills in what happens before it: how a computer gets from the power button to a running system, and what the things you list with ps actually are. Two ideas from here come up in the theory tests again and again – the boot sequence, and the difference between a process and a thread.

In two lines: booting is a relay – firmware → bootloader → kernel → init → login → your session, each stage starting the next. A process is a program running in its own protected memory; threads are several flows of execution inside one process, sharing that memory.

From the power button to a prompt

power onfirmwareUEFI / BIOS · hardware checkbootloaderloads the kernelkerneldrivers · memory · processesno operating system yetthe OS takes overstarts the first user-space processinit / systemdPID 1 · starts the servicesloginwho are you?your sessiona shell over SSH, or a desktop
Fig. 1 · Each stage only has to know how to start the next one.

1. Firmware: UEFI or BIOS

When you press the power button no operating system is running yet; the code that runs first is the firmware stored on the motherboard – UEFI on modern machines, BIOS on older ones. It initialises and checks the hardware it needs: CPU, RAM, graphics, storage. For RAM this can include memory training, where the firmware tunes the timing of the memory modules. If something fails here, there is no screen to print an error on yet, so the board reports it with an error code, an LED or a pattern of beeps.

2. Boot device and bootloader

Based on its configuration (the boot order), the firmware looks for a device the operating system can be started from. On a UEFI machine this is usually an EFI bootloader on the SSD – GRUB for many Linux systems, the Windows Boot Manager for Windows. The bootloader's single job is to load the operating system's kernel into memory and start it. It is also where a dual-boot menu lets you choose which system to start.

3. Kernel

The kernel takes control of the machine. It initialises the hardware, loads the drivers (on Linux: kernel modules) and sets up the core services of the operating system: memory management, processes and scheduling, file systems, networking. Then it starts the first program that runs outside the kernel, in user space.

4. init / systemd – PID 1

On Linux that first program is init – on most current distributions, systemd. In the classic Unix model init has PID 1, and systemd runs as PID 1 too. It starts the rest of the system as services (daemons): networking, the SSH server, logging, the services a graphical desktop needs, and many more background processes.

$ ps -p 1 -o pid,comm
    PID COMMAND
      1 systemd

5. Login

The system reaches a login screen or prompt. You authenticate, and the system starts the processes your session needs.

6. Desktop environment – or a shell

On a graphical Linux system a desktop environment such as GNOME or KDE Plasma starts: windows, a panel, a file manager – the place where you click and start applications, with more user and system processes starting in the background. A server like the course server usually has no desktop at all. There the session is a shell, started for you by the SSH server when you log in.

Where your shell comes from

Every process except PID 1 was started by another process, its parent. So the shell you type into on the course server is the end of a chain that starts at systemd (Fig. 2).

systemdPID 1 – the ancestor of allsshdwaits for connectionssshdyour connectionbashyour shell – echo $$ls, ./script.sh …what you startother servicesjournald, cron, …every process has a parent:its PPID is the parent's PID
Fig. 2 · Your shell and everything you start from it are leaves of one tree rooted at PID 1.

$$ holds the PID of the current shell; ps -o pid,ppid,comm shows each process with its parent's PID (PPID). pstree, where installed, draws the chain:

$ echo $$                       # the PID of your shell
24518
$ pstree -s -p $$               # its ancestors, up to PID 1 (if pstree is installed)
systemd(1)───sshd(912)───sshd(24511)───sshd(24517)───bash(24518)───pstree(24690)
$ ps -o pid,ppid,comm
    PID    PPID COMMAND
  24518   24517 bash
  24702   24518 ps

Note that ps itself shows up as a child of bash: it's a process you just started. That is the topic of the next sections.

Processes

A process is a running program together with its own, isolated execution environment: its own virtual address space, its own resources (open files, current directory, environment variables) and a process identifier, the PID. Two processes running the same program – two shells, say – are still two processes with separate memory.

"Virtual" means each process sees memory addresses of its own, as if it had the whole memory to itself. The hardware (the MMU) and the kernel map those addresses to physical RAM, and one process can't reach another's memory by default. That isolation is why a crashing program doesn't take the others down.

high addresseslow addresseskernel spacenot accessible from the programstack ↓local variables, return addressesfreeshared libraries; room for both to growheap ↑allocated at run time (malloc, new)dataglobal and static variablestextthe machine code – read-only
Fig. 3 · The address space of a process. The stack and the heap grow towards each other.

Creating a process: fork()

On POSIX systems (Linux, macOS, the Unixes) the classic way to create a process is the fork() system call. The calling process gets a child: an almost exact copy of itself with a new PID. Both continue from the same point; fork() returns 0 in the child and the child's PID in the parent, which is how each knows which one it is.

A copy of the whole memory sounds expensive, and it would be. It doesn't happen: modern systems use copy-on-write (COW). Parent and child initially share the physical memory pages, marked read-only; a page is really copied only when one of them writes to it.

right after fork() – pages shared, read-onlyparentPID 100childPID 101page Apage Bafter the child writes to page BparentPID 100childPID 101page Apage Bcopy of B
Fig. 4 · Copy-on-write: fork() copies the mapping, not the memory. Pages are duplicated lazily, one at a time.

Running another program: exec

fork() gives you a copy of the same program. To run a different one, the child calls one of the exec functions, which replace the program in the process – code, data, stack – with a new one, while the PID stays. Every time you type a command, the shell does exactly this:

bashPID 100 · your shellwait() – blocked until the child endsbashnext promptfork()bash (copy)PID 101 · after forkexeclsPID 101 · new programexit → $?The PID survives exec: exec replaces the program running in the process, not the process.
Fig. 5 · fork + exec + wait: the three steps behind every command you type.

This also explains two things from the practical sessions. A script run as ./script.sh runs in a child process, so its cd or variable changes vanish when it ends – while source script.sh runs it in your current shell. And $? is simply the exit status the parent received from wait().

Threads

A thread is a unit of execution inside a process. One process can run several threads, in parallel on several CPU cores or interleaved on one. The threads share the address space and most resources of their process – code, heap, global variables, open files – so creating one doesn't involve setting up a new address space.

What each thread has of its own is its execution state: its own stack, its own register values and its own program counter (instruction pointer) – the place in the code where it currently is. A program can start several threads and give each a different job: one waits for the network, one does the calculation, one keeps the user interface responsive.

two processes – separate memorymemory of Acode, heap, data, files1 threadstack, registersmemory of Bcode, heap, data, files1 threadstack, registersone process, three threadsshared memorycode, heap, globals, open filesthread 1own stackthread 2own stackthread 3own stack
Fig. 6 · Processes are separated by default; threads of one process share by default.
ProcessThread
An independent execution environmentA unit of execution inside a process
Its own virtual address spaceShares the process's address space
Stronger isolationEasier communication
Usually more expensive to createUsually cheaper to create
Its own PIDIts own execution state and thread ID
Can't see another process's memory by defaultSees the process's shared memory

An analogy: a process is a separate office with its own room and equipment. Threads are people working in the same office – they use the shared equipment, but each has a task of their own and knows where they are in it.

Seeing threads on Linux

A shell has a single thread; a browser or a database server has dozens. Linux calls threads lightweight processes (LWP):

$ ps -o pid,nlwp,comm -p $$          # NLWP = number of threads
    PID NLWP COMMAND
  24518    1 bash
$ grep Threads /proc/$$/status
Threads:	1
$ ps -eLf | head -5                   # one row per thread (LWP = thread id)

and the services systemd started

systemctl list-units --type=service --state=running   # what systemd started
journalctl -b | head                                  # the log of this boot (may need permissions)

Terms

firmware
Code on the motherboard (UEFI or BIOS) that runs first, checks the hardware and starts a bootloader.
bootloader
A small program (e.g. GRUB) that loads the kernel into memory and starts it.
kernel
The core of the operating system: hardware, memory, processes, file systems, network.
PID 1
The first user-space process – init or systemd – ancestor of all others.
daemon
A background service, usually started by PID 1 (sshd, cron, journald).
process
A running program with its own virtual address space, resources and PID.
thread
A flow of execution inside a process, with its own stack and registers.
fork()
Creates a child process as a copy of the caller.
copy-on-write
Pages are shared after fork and copied only when written.
exec
Replaces the program running in a process; the PID stays.

Summary

  • Booting is a relay: firmware → bootloader → kernel → init/systemd (PID 1) → login → shell or desktop.
  • Every process has a parent; your shell's chain ends at PID 1 – check it with pstree -s $$.
  • A process owns a virtual address space, resources and a PID, isolated from other processes.
  • fork() copies a process (lazily, with copy-on-write), exec swaps its program, wait() collects its exit status – the shell does all three for every command.
  • Threads share their process’s memory and files; each has its own stack, registers and program counter.