Chapter 12 — Conclusion: The Full Stack Mental Model

Assembling the Picture

You’ve now seen every layer of the Linux hardware stack. Let’s assemble them into one coherent mental model.

┌─────────────────────────────────────────────────────────────┐
│                     Your Python Code                        │
│    open()   read()   write()   ioctl()   mmap()   socket()  │
└───────────────────────────┬─────────────────────────────────┘
                            │ system calls (Ring 3 → Ring 0)
┌───────────────────────────▼─────────────────────────────────┐
│                   Linux Kernel (Ring 0)                     │
│                                                             │
│  ┌─────────────┐  ┌──────────────┐  ┌───────────────────┐  │
│  │     VFS     │  │  Scheduler   │  │  Memory Manager   │  │
│  └──────┬──────┘  └──────────────┘  └───────────────────┘  │
│         │                                                   │
│  ┌──────▼─────────────────────────────────────────────┐    │
│  │              Filesystem Layer                       │    │
│  │  ┌────────┐  ┌────────┐  ┌──────┐  ┌──────────┐   │    │
│  │  │  ext4  │  │ sysfs  │  │procfs│  │devtmpfs  │   │    │
│  │  │ (disk) │  │ (/sys) │  │(/proc│  │  (/dev)  │   │    │
│  │  └────────┘  └────────┘  └──────┘  └──────────┘   │    │
│  └──────────────────────────────────────────────────┘    │
│                                                             │
│  ┌──────────────────────────────────────────────────────┐  │
│  │                  Device Drivers                      │  │
│  │  NVMe   SATA   e1000e   i915   cp210x   snd_hda  ... │  │
│  └──────────────────────┬───────────────────────────────┘  │
│         DMA / MMIO / Interrupts                            │
└──────────────────────────┬──────────────────────────────────┘
                           │
┌──────────────────────────▼──────────────────────────────────┐
│                    Hardware Buses                            │
│                                                             │
│   PCIe ──── GPU, NVMe, NIC, Audio                          │
│   USB  ──── Keyboard, Storage, Serial adapters             │
│   I2C  ──── Sensors, OLED, RTC                             │
│   SPI  ──── Flash, ADC, Display                            │
│   SATA ──── Hard drives, SSDs                              │
└─────────────────────────────────────────────────────────────┘

The Key Relationships

Python → Kernel: Via system calls. Every open(), read(), write() crosses the Ring 3/Ring 0 boundary. Watch them with strace.

Kernel → Filesystems: The VFS routes all file operations. /sys, /proc, /dev are virtual filesystems — no disk involved.

/dev: Device files created by drivers. Major number identifies the driver; minor number identifies the instance. Open them like regular files.

/sys: The kernel’s hardware object model. One directory per device, one file per attribute. Read hardware state; write to control hardware.

/proc: Process information and kernel state. /proc/cpuinfo, /proc/meminfo, /proc/interrupts, /proc/iomem are your hardware inspection tools.

Drivers → Hardware: Drivers use MMIO (read/write device registers like memory), port I/O (legacy), and DMA (hardware writes directly to RAM). Interrupts tell the CPU when hardware needs attention.

udev: The userspace daemon that watches kernel uevents, creates /dev entries, sets permissions, and fires rules. The bridge between hardware events and your system.

The Debugging Path

When something hardware-related isn’t working, follow this path:

1. Is the hardware physically detected?
   → lspci / lsusb

2. Is there a driver loaded?
   → lspci -k / lsmod / dmesg

3. Is a /dev file created?
   → ls /dev/  /  udevadm info

4. Do you have permission?
   → ls -la /dev/device  /  groups

5. Is the driver working?
   → dmesg -w while testing  /  journalctl -k

6. Can Python open it?
   → try open("/dev/...") with explicit error handling

The “Everything Is a File” Payoff

After reading this book, the principle “everything is a file” should feel concrete rather than abstract:

# Get CPU temperature — file read
temp = int(Path("/sys/class/thermal/thermal_zone0/temp").read_text()) / 1000

# Monitor USB plug events — file read from a socket-like fd
for event in pyudev.Monitor.from_netlink(context):
    print(event.action, event.sys_name)

# Read random bytes from hardware RNG — file read
entropy = open("/dev/urandom", "rb").read(32)

# Control LED brightness — file write
Path("/sys/class/leds/capslock/brightness").write_text("1")

# Read raw keypress events — file read with struct parsing
data = os.read(open("/dev/input/event0", "rb").fileno(), 24)

# Map hardware registers — file mmap
mm = mmap.mmap(open("/sys/bus/pci/devices/0000:00:02.0/resource0", "rb").fileno(), ...)

All of these are the same operation — file I/O — at different levels of the stack.

What’s Beyond This Book

If this book has made you curious, here are the natural next steps:

Kernel driver development: Learn to write C kernel modules. “Linux Device Drivers” (3rd ed., free online) and the kernel documentation at kernel.org are the canonical resources.

Embedded Linux: Apply these concepts to ARM boards (Raspberry Pi, BeagleBone, RISC-V boards). Device Tree replaces PCI discovery for embedded hardware.

Real-time Linux: Understand PREEMPT_RT, interrupt latency, CPU isolation, and cyclictest for deterministic hardware control.

Performance tuning: Use perf, ftrace, and eBPF to profile hardware interactions and optimize I/O paths.

Hardware security: Understand IOMMU, DMA remapping, PCIe ACS, and why physical access to hardware is equivalent to root access.

One Last Thing

The most important tool isn’t a library or a command — it’s curiosity. When you see a file in /sys you don’t recognize, read it. When dmesg shows a line you don’t understand, search it. Every mystery in the /sys and /proc trees has a well-documented answer in the kernel source and documentation.

The hardware is talking. Now you know how to listen.


Previous: Chapter 11 — Practical Examples

Back to Table of Contents