Devices tell the O/S that service is needed by issuing an interrupt.
Alternatively the CPU can poll the device
looking for a status bit that says the device needs service.
Interrupts are vectored by the CPU to a specific memory location assigned
in the interrupt vector table. The O/S programmer
must write the Interrupt Service routine (ISR)
to handle the interrupt for that device and put its location (the interrupt
vector) in the vector table entry at the location of the vector
adress returned by the device.
Exceptions and traps
are handled in the same way as interrupts. Exceptions are generated
by the CPU normally in response to errors, and traps are generated by software.
Devices are communicated with and controlled by reading and writing
data from/to their registers.
Those registers can appear to be in the same physical address space
as memory and therefore can be read and written using normal memory operations.
Alternatively, some (most) CPUs provide special read/write instructions
to communicate with devices not living in the regular memory address space.
The CPU may have to (or choose to) read/write data from/to the controller
itself. This is called programmed I/O.
Some controllers can read/write memory directly (without the help of
the CPU). This is called DMA and it frees
the CPU to execute other processess while the transfer is taking place.
Why do we care about all this? Because
ALL of this must be considered when writing a device driver (software
that controls a device).