@zkat Going entirely from memory here, I may be wrong on the details:
From what I recall, it depends on the architecture. For example ARM systems often have specifically allocated memory regions (at fixed addresses) that represent input/output buffers for peripherals (and that are interacted with through regular memory writes/reads), but eg. x86 has "I/O ports" that require specific CPU instructions to interact with.
I *think* that on "non-embedded" ARM stuff (like ARM-based desktops or phones) it still works the same as on embedded ARM microcontrollers, with the specially-allocated memory regions.