Interview Prep Zoneby CuriouserLabs

Question 4 of 5 · staff level

Memory-mapped I/O vs port I/O: how does software actually control a device?

🎤 Say this first

Devices expose registers (control, status, doorbells) and sometimes buffers; the CPU needs a way to read/write them. Port I/O (x86 legacy): a separate tiny address space with special in/out instructions — how PCs configured devices for decades. Memory-mapped I/O (universal now): device registers are assigned physical memory addresses — ordinary loads/stores hit the device instead of RAM, so drivers are normal(ish) code and any addressing mode works. The critical caveats: MMIO regions must be uncached and un-reordered (a 'read status register' load has side effects; caches/write-combining would break the protocol) — which is why barriers and volatile-style access discipline pervade driver code.

The full picture

  • How it fits together on PCIe: firmware/OS enumerate devices and program BARs (base address registers) — carving each device's register windows into the physical address map; the driver ioremaps them and pokes structs-at-addresses. lspci -v shows these windows on any Linux box — a nice 'I've actually looked' detail.
  • Why side-effectful addresses change the rules: reading a status register can pop a FIFO; writing a doorbell launches a command. Hence uncacheable mappings, explicit read/write barriers, and 'every access exactly as written' — the same reasoning as volatile, at its origin (this is literally where C's volatile came from).
  • Doorbell + ring idiom completes the picture: driver writes descriptors in RAM (cacheable, fast), then one MMIO write to the doorbell says 'go' — minimizing slow uncached accesses. NVMe submission works exactly like this; it's why understanding MMIO explains modern storage stacks.
  • User-space I/O is MMIO democratized: DPDK/SPDK map device BARs into a process and drive NICs/NVMe without the kernel — the logical endpoint of 'registers are just memory'.

🔄 Likely follow-up questions

  • Why must MMIO regions be uncacheable, with a concrete corruption scenario?
  • What does ioremap/write-combining do for framebuffer-style workloads?
  • How does C's volatile keyword relate to (and differ from) Java's?