﻿# 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.

Source: https://laszlonemes.com/university/computer-systems/boot-processes-threads · Author: László Nemes

Reading · Computer Systems

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 read `UEFI/BIOS → bootloader → kernel` `systemd as PID 1` `Process address space` `fork, copy-on-write, exec` `Process 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.

After this session you can

- name the stages of booting in order and say what each one hands to the next
- explain why `systemd` (or `init`) has PID 1, and trace your own shell back to it
- describe what a process owns: an address space, resources, a PID
- explain `fork()`, copy-on-write and `exec` – and how the shell uses them to run a command
- say what threads of one process share and what each has of its own

> **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

> **Fig. 1** · Each stage only has to know how to start the next one.
>
> *Diagram:* Booting is a relay. Power on starts the firmware, UEFI or BIOS, which checks the hardware and starts a bootloader from the boot device. The bootloader loads the kernel. The kernel sets up drivers, memory and processes and starts init or systemd as PID 1, which starts the services and the login. After login you get your session: a shell over SSH, or a desktop.

### 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.

```text
$ 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.

Good to know

**The whole chain in one line**

Power → UEFI/BIOS → hardware initialisation → bootloader → kernel → system services → login → desktop environment or shell → your applications.

## 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).

> **Fig. 2** · Your shell and everything you start from it are leaves of one tree rooted at PID 1.
>
> *Diagram:* The process tree behind an SSH login. systemd, PID 1, started the SSH daemon. For your connection sshd started a process of its own, which started your bash shell. Every command you type is a child of that shell. systemd also started many other services side by side.

`$$` 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:

```text
$ 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)
```

```text
$ 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.

> **Fig. 3** · The address space of a process. The stack and the heap grow towards each other.
>
> *Diagram:* The virtual address space of one process, from high to low addresses: kernel space the program cannot touch, the stack growing down, free space where shared libraries are mapped, the heap growing up, global data, and the read-only program code at the bottom.

### 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.

> **Fig. 4** · Copy-on-write: fork() copies the mapping, not the memory. Pages are duplicated lazily, one at a time.
>
> *Diagram:* Copy-on-write. Right after fork the parent and the child point at the same physical memory pages, marked read-only. When the child writes to page B, only that page is copied, and the child's mapping moves to the copy. Page A is still shared.

### 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:

> **Fig. 5** · fork + exec + wait: the three steps behind every command you type.
>
> *Diagram:* How the shell runs ls. bash calls fork and gets a child, an identical copy with a new PID. The child calls exec, which replaces its program with ls but keeps the PID. The parent waits; when ls exits, its exit status reaches the shell as $? and the next prompt appears.

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()`.

Good to know

**Windows does it differently**

Windows has no POSIX-style `fork()`. A new process is created with `CreateProcess()`, which starts a given program in a fresh process with its own virtual address space – roughly fork and exec in one call.

## 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.

> **Fig. 6** · Processes are separated by default; threads of one process share by default.
>
> *Diagram:* Two processes each have their own memory and one thread; neither sees the other's memory. One process with three threads has one shared memory – code, heap, globals, open files – and each thread has only its own stack and registers.

| Process | Thread |
| --- | --- |
| An independent execution environment | A unit of execution inside a process |
| Its own virtual address space | Shares the process's address space |
| Stronger isolation | Easier communication |
| Usually more expensive to create | Usually cheaper to create |
| Its own PID | Its own execution state and thread ID |
| Can't see another process's memory by default | Sees the process's shared memory |

Common mistake

**Shared memory cuts both ways**

Because threads see the same variables, two threads updating one value at the same time can lose an update (a race condition). Threads make communication easy and correctness harder: shared data needs synchronisation, such as locks. Processes avoid the problem by not sharing in the first place.

**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):

```text
$ 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

```shell
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.
