In short
The I/O system is the part of the operating system that moves data between the CPU/memory and the outside world - keyboards, disks, NICs, printers, touchscreens, sensors - and hides the differences between hundreds of device types behind a few uniform interfaces (read, write, open, close, ioctl). It matters because I/O, not computation, is the bottleneck in most real systems: a CPU instruction takes a nanosecond, a disk access milliseconds, so the design question is always "how do we avoid making the CPU wait?" In exams it appears as programmed I/O vs interrupt-driven I/O vs DMA, interrupt handling steps, buffering/spooling and device-driver structure; GATE sets numericals on data-transfer rates, DMA cycle stealing and interrupt overhead. In placement rounds (TCS, Infosys, Wipro, Cognizant, Qualcomm, Samsung R&D, Bosch, Tata Elxsi, Zoho, embedded and firmware roles) the standard questions are "what is DMA and why use it", "polling vs interrupt", "how does a device driver work" and "what is memory-mapped I/O".
Explained
Hardware: devices, controllers, buses and ports
- A device rarely talks to the CPU directly. It connects to a device controller (host adapter) - a small processor with its own registers and local buffer - which sits on a bus.
- Four standard controller registers: data-in (device → CPU), data-out (CPU → device), status (busy, ready, error, interrupt-pending bits) and control/command (what operation to perform).
- An I/O port is the address of such a register. Two addressing styles:
- Port-mapped I/O (isolated I/O): a separate I/O address space with special instructions (
IN,OUTon x86). Keeps memory space free; needs extra instructions and pins. - Memory-mapped I/O: device registers are assigned ordinary physical memory addresses, so normal
MOV/load-store instructions access them. Simpler, allows pointer arithmetic and large regions (a frame buffer); costs part of the address space. ARM-based systems (most Indian embedded and mobile work) are almost entirely memory-mapped.
- Port-mapped I/O (isolated I/O): a separate I/O address space with special instructions (
- Registers accessed by memory-mapped I/O must be declared **
volatile** in C, and must not be cached - the value can change without the CPU writing it. - Buses: a shared set of wires with a protocol. Historically ISA/PCI; today PCIe (point-to-point serial lanes), USB (tiered-star, polled by the host), SATA/NVMe for storage, I2C/SPI/UART on microcontrollers. Bus arbitration (daisy chain, polling, independent request) decides which master drives the bus.
- Device classification:
- Block devices - fixed-size addressable blocks, random access (disk, SSD, SD card).
- Character (stream) devices - byte stream, no seek (keyboard, mouse, serial port, sound).
- Network devices - packet interface, accessed via sockets, not a plain file.
- Clock/timer devices - current time, elapsed time, triggers (programmable interval timer).
- Also shared vs dedicated, virtual devices (a spooled printer), and sequential vs random access.
The three techniques of data transfer
1. Programmed I/O (polling / busy-waiting)
- Handshake: CPU reads the status register in a loop until the busy bit clears; writes data into data-out; sets the command-ready bit; controller sets busy, performs the transfer, clears busy and command-ready.
- Every byte costs several CPU instructions. Simple, no extra hardware, deterministic - so it is still used for very fast devices (where an interrupt costs more than the wait) and in boot code or tiny microcontrollers.
- Wastes CPU time badly for slow devices; cannot overlap I/O with computation.
2. Interrupt-driven I/O
- The CPU issues the command and continues with other work. When the device is ready, it raises a signal on the interrupt-request line; the CPU checks this line after every instruction.
- The CPU saves state, jumps through the interrupt vector table to the interrupt service routine (ISR), the ISR moves the data, then control returns via
IRET. - Needs interrupt priority levels so an urgent device (timer, disk) pre-empts a slow one, and maskable vs non-maskable interrupts - NMI is reserved for unrecoverable events (memory parity error, watchdog).
- Removes busy-waiting, but still one interrupt per byte/word in the worst case; interrupt overhead (context save, cache/TLB pollution) dominates at high data rates.
3. Direct Memory Access (DMA)
- A DMA controller transfers a whole block between device and memory without the CPU touching each word.
- Steps: (i) CPU programs the DMA controller with source address, destination address, byte count and direction, then continues; (ii) DMA controller requests the bus (HOLD/HRQ), CPU grants it (HLDA); (iii) controller transfers words directly to/from memory; (iv) on completion it raises one interrupt; (v) the driver's completion routine finishes up.
- Transfer modes:
- Burst (block) mode - the whole block in one bus acquisition; fastest, but starves the CPU meanwhile.
- Cycle stealing - one word per bus acquisition; CPU and DMA interleave, so the CPU slows down slightly instead of stalling.
- Transparent (hidden) mode - transfers only in cycles when the CPU does not need the bus; no CPU slowdown but slowest.
- Cycle stealing is the usual exam answer for "how does DMA avoid stopping the CPU".
- Scatter-gather DMA uses a list of address/length descriptors, so one request can fill many non-contiguous page frames - essential with paging.
- Cache coherence problem: DMA writes to memory behind the CPU's back, so stale cache lines must be invalidated (hardware snooping) or flushed by the driver. Similarly an IOMMU (SMMU on ARM) translates device addresses, giving protection and allowing devices to use virtual addresses.
- Channel I/O (mainframes) goes one step further: an I/O processor executes a channel program of several commands independently.
Interrupt mechanism in detail
- Types: hardware interrupts (external devices), traps/exceptions (software-generated - divide by zero, page fault), software interrupts (
INT n,svc- the system-call mechanism), vectored (device supplies its vector number) vs non-vectored (fixed address). - Steps on an interrupt: finish current instruction → save PC and PSW (and registers) → disable or mask same/lower-priority interrupts → fetch vector → execute ISR → acknowledge device → restore state →
IRET. - Interrupt chaining: several devices share one vector; the handler walks a list of ISRs.
- Interrupt controller: the 8259 PIC (8 lines, cascadable to 64), now the APIC/GIC on x86/ARM; MSI/MSI-X on PCIe lets a device write a message to a special address instead of asserting a pin - thousands of vectors and better multi-core steering.
- Top half / bottom half (Linux): the ISR (top half) does the minimum with interrupts disabled; deferred work goes to softirqs, tasklets or workqueues (bottom half) so interrupts are re-enabled quickly. Low interrupt latency and short ISRs are what make a system real-time capable.
- Interrupt coalescing / NAPI: at high packet rates the NIC raises one interrupt and the driver then polls, combining the strengths of both techniques.
Kernel I/O subsystem: software layers and services
Layering (bottom to top): device hardware → device controller → device driver → device-independent kernel I/O subsystem → system-call interface → user-level I/O library (stdio) → application.
- Device driver: the only code that knows the device's register layout. It exports a fixed entry-point table (
open,release,read,write,ioctl,mmap,poll, plus the interrupt handler) so the kernel treats all devices alike. In Linux a driver is usually a loadable kernel module (insmod,rmmod,lsmod) and runs in kernel mode - a bug crashes the system, which is why bugs in drivers cause a large share of OS crashes. - Device-independent services provided by the kernel:
- Naming and protection - Linux exposes devices as special files in
/devwith a major number (which driver) and minor number (which unit); access is controlled by ordinary file permissions./sysandudevhandle dynamic naming. - Buffering - a memory area between producer and consumer. Needed for three reasons: (i) speed mismatch between device and application, (ii) different data-transfer sizes (fragmented packets vs whole messages), (iii) copy semantics - the kernel copies the user buffer at the
write()call so later user changes do not corrupt the write. Single buffering, double buffering (fill one while draining the other), circular buffering (many buffers, used for bursty producers). - Caching - a copy of data held for speed (the page cache / buffer cache). A buffer may be the only copy; a cache is always a duplicate. Writes may be write-through or write-back;
sync/fsyncforces them out. - Spooling (Simultaneous Peripheral Operation On-Line) - requests to a non-shareable device are queued as separate files and served one at a time; the classic case is a printer, where each job becomes a spool file (CUPS on Linux, the print spooler on Windows). The device appears virtual and shareable.
- Device reservation - exclusive allocation of a dedicated device, with the risk of deadlock if two processes hold and wait.
- Error handling - retries, error codes (
errno:EIO,EAGAIN,ENODEV), SCSI sense data, logging. - I/O scheduling - reordering the request queue for fairness and throughput (the per-device request queue; disk-specific algorithms belong to disk scheduling).
- Power management and hot-plug - suspend/resume callbacks, enumeration on insertion (USB).
- Naming and protection - Linux exposes devices as special files in
- HAL / abstraction layer: the thin layer that isolates machine-dependent register access, so the same driver source builds on different boards.
Blocking, non-blocking and asynchronous I/O
- Blocking: the calling process is moved to the wait queue until the I/O completes. Simplest to program; the normal behaviour of
read()on a file. - Non-blocking: the call returns immediately with however many bytes are available (possibly 0) -
O_NONBLOCK, used withselect/poll/epollfor event-driven servers. - Asynchronous: the call returns at once and the I/O completes later, with notification by signal, callback or completion queue (POSIX AIO, Linux
io_uring, Windows overlapped I/O). The process must not touch the buffer until notified. - Note the distinction: non-blocking returns *partial* data now; asynchronous returns *no* data now but does the full transfer in the background.
- Synchronous vs asynchronous at the device level: a synchronous device transfers data with predictable response time; an asynchronous one responds irregularly and must signal completion.
- Life cycle of a blocking read: system call → kernel checks the buffer/page cache → if a hit, copy and return; else build a request, queue it to the driver, block the process → driver programs the controller/DMA → device completes, raises an interrupt → ISR/bottom half copies data, marks the request done → kernel moves the process from wait queue to ready queue → the
read()returns.
Performance, I/O protection and transformation of a request
- Where time goes: context switches, data copying (user ↔ kernel ↔ device), interrupt handling, and the device's own service time. Each of these is a lever.
- Ways to improve I/O performance: reduce context switches; reduce data copying (zero-copy,
sendfile,mmap, DMA straight into user pages); reduce interrupts with large transfers, smart controllers and polling where appropriate; use DMA; balance CPU, bus, memory and device speeds; move processing into the controller or a dedicated processor. - I/O protection: all I/O instructions are privileged. A user process cannot touch a device register directly; it must issue a system call, which switches to kernel mode. Memory-mapped device regions are protected by the MMU page tables, so a user program is only given access if the kernel maps it deliberately. This is why "all I/O instructions privileged" is the standard one-line answer on protection.
- Transformation of an I/O request to hardware operations: a user's
read()on a file name is mapped by the file system to a device (major/minor), then to a block number, then to a request on that device's queue, then to controller commands, then to an interrupt and finally back to the process - the chain that exam questions call "the life cycle of an I/O request". - STREAMS (UNIX System V): a full-duplex pipeline between user process and driver, built from pushable modules each with a read and write queue - a software framework for layered character I/O (used for TTY and networking stacks).
- Indian practice note: firmware/driver work in India follows the same POSIX/Linux and ARM conventions used worldwide; where Indian standards enter is in device certification (BIS registration under the Compulsory Registration Scheme for IT hardware, TEC/MTCTE approval for telecom equipment, WPC approval for radio modules) rather than in the OS interface itself. Exact scope of these schemes is revised periodically.
Key points
- Device controller - hardware between device and bus, with data-in, data-out, status and control registers.
- I/O port - address of a controller register; memory-mapped I/O gives it a memory address, port-mapped I/O a separate I/O space.
- Programmed I/O - CPU polls the status bit and moves every byte itself; simple, but burns CPU cycles.
- Interrupt-driven I/O - device signals readiness; CPU runs an ISR per byte/word; overlaps I/O with computation.
- DMA - controller moves a whole block to/from memory, one interrupt at the end; CPU only sets up and completes.
- Cycle stealing - DMA takes the bus one word at a time, so the CPU is slowed, not stopped; burst mode is fastest but starves the CPU.
- Scatter-gather + IOMMU - let DMA work with paged, virtual, non-contiguous memory safely.
- Interrupt vector table - maps interrupt number to ISR address; vectored interrupts supply their own number.
- Top half / bottom half - short ISR plus deferred softirq/tasklet/workqueue, to keep interrupt latency low.
- Device driver - device-specific code with a standard entry-point table (
open/read/write/ioctl/release); a loadable module in Linux. - Major and minor numbers - identify the driver and the specific unit behind a
/devspecial file. - Buffering - handles speed mismatch, size mismatch and copy semantics; single, double and circular schemes.
- Buffer vs cache - a buffer may hold the only copy of data; a cache always holds a duplicate kept for speed.
- Spooling - queues jobs as files for a non-shareable device (printer), making it appear shareable.
- Blocking / non-blocking / asynchronous - wait for completion / return what is available now / be notified later.
- I/O instructions are privileged - user processes reach devices only through system calls, which is the basis of I/O protection.
- Block vs character device - addressable fixed-size blocks with random access vs a sequential byte stream.
- Performance rule - cut context switches, copies and interrupts; move work into the controller.
Formulas and examples
CPU time for interrupt-driven I/O = (number of transfers) × (interrupt overhead + ISR time); number of transfers = total bytes / bytes per interrupt
CPU time for DMA = setup time + completion-interrupt time (independent of block size)
Device data rate R = bytes per second; time to transfer N bytes = N / R
DMA bus cycles stolen per block = block size (bytes) / bus width (bytes per cycle)
CPU slowdown with cycle stealing (%) = (cycles stolen per second / total CPU cycles per second) × 100
Effective access time with buffering/cache = h × t_cache + (1 − h) × t_device, where h = hit ratio
Interrupts per second = device rate / transfer size per interrupt
Throughput utilisation of a device = (useful transfer time) / (useful transfer time + overhead time)
Example 1 - Polling vs interrupts for a keyboard. A keyboard delivers at most 100 characters per second. Polling the status register costs 50 CPU cycles per poll and the OS polls 1,000 times per second; an interrupt costs 400 cycles (save/restore + ISR). CPU runs at 1 GHz (10⁹ cycles/s).
- Polling: 1,000 × 50 = 50,000 cycles/s → 50,000/10⁹ = 0.005% of the CPU, but 900 of the 1,000 polls find nothing.
- Interrupts: 100 × 400 = 40,000 cycles/s → 0.004% of the CPU, and no wasted polls.
- Conclusion: for a slow, bursty device interrupts win; the margin here is tiny, which is why polling survives in simple firmware.
Example 2 - Interrupt-driven I/O vs DMA for a disk block. Transfer 4 KB (4,096 bytes) from disk. Interrupt-driven I/O moves 2 bytes per interrupt; each interrupt costs 1,000 cycles. DMA needs 1,500 cycles of setup and 1,000 cycles for the completion interrupt. CPU = 2 GHz.
- Interrupts needed = 4,096 / 2 = 2,048; cycles = 2,048 × 1,000 = 2,048,000 → 2,048,000 / (2 × 10⁹) = 1.024 ms of CPU time.
- DMA cycles = 1,500 + 1,000 = 2,500 → 2,500 / (2 × 10⁹) = 1.25 µs.
- DMA uses 2,048,000 / 2,500 = 819.2 times less CPU time. This ratio is the standard "why DMA" answer.
Example 3 - Cycle stealing and CPU slowdown. A device transfers 40 MB/s (40 × 10⁶ bytes/s) by DMA in cycle-stealing mode. The bus moves 4 bytes per cycle and the CPU runs at 500 MHz, one bus cycle per CPU cycle.
- Cycles stolen per second = 40 × 10⁶ / 4 = 10 × 10⁶ cycles/s.
- CPU cycles available = 500 × 10⁶/s → slowdown = 10 × 10⁶ / 500 × 10⁶ = 2%.
- In burst mode the same block would stall the CPU completely for the duration of each burst; the average loss is similar but the latency is much worse - a real-time system therefore prefers cycle stealing.
Example 4 - Interrupts per second and ISR budget for a NIC. A 1 Gbps Ethernet NIC receives minimum-size frames of 64 bytes (plus 20 bytes of inter-frame gap and preamble ⇒ 84 bytes on the wire = 672 bits).
- Maximum frames per second = 10⁹ / 672 ≈ 1.488 × 10⁶ frames/s.
- One interrupt per frame leaves 1/1.488 × 10⁶ ≈ 0.672 µs per interrupt - far less than a typical 1-5 µs interrupt cost, so the CPU is saturated (livelock).
- With coalescing at 64 frames per interrupt: 1.488 × 10⁶/64 ≈ 23,250 interrupts/s, i.e. 43 µs apart - comfortable. This is why NAPI/coalescing exists.
Example 5 - Effective access time with a buffer cache. A block read hits the page cache 85% of the time (hit cost 1 µs); a miss costs about 8 ms from an HDD, or about 0.1 ms (100 µs) from a SATA SSD - both are worked below for comparison.
- With HDD: EAT = 0.85 × 1 µs + 0.15 × 8,000 µs = 0.85 + 1,200 = 1,200.85 µs ≈ 1.2 ms.
- With SSD: EAT = 0.85 × 1 µs + 0.15 × 100 µs = 0.85 + 15 = 15.85 µs.
- Raising the hit ratio to 95% with the HDD: 0.95 × 1 + 0.05 × 8,000 = 400.95 µs - a 10-point gain in hit ratio cuts the time by two-thirds, because the miss cost dominates.
| Technique | Who moves the data | Interrupts per block | CPU cost | Extra hardware | Typical use |
|---|---|---|---|---|---|
| Programmed I/O (polling) | CPU, in a loop | 0 | Very high | None | Boot code, tiny MCUs, very fast devices |
| Interrupt-driven I/O | CPU, inside ISR | One per byte/word | High at high rates | Interrupt controller | Keyboard, mouse, UART, low-rate sensors |
| DMA | DMA controller | One per block | Very low | DMA controller, bus arbiter | Disk, SSD, NIC, audio, graphics |
| Channel I/O | I/O processor | One per channel program | Lowest | I/O processor | Mainframes |
| Feature | Memory-mapped I/O | Port-mapped I/O |
|---|---|---|
| Address space | Shares physical memory space | Separate I/O space |
| Instructions | Normal load/store | Special IN/OUT |
| Address space cost | Consumes memory addresses | None |
| Large regions (frame buffer) | Easy | Awkward |
| Typical platform | ARM, RISC-V, PCIe BARs | Legacy x86 peripherals |
| Aspect | Buffer | Cache | Spool |
|---|---|---|---|
| Purpose | Smooth speed/size mismatch | Speed up repeat access | Serialise jobs for a dedicated device |
| Copy status | May be the only copy | Always a duplicate | A queued job file |
| Example | UART ring buffer | Page cache | CUPS print queue |
Where it is used
- Embedded and automotive firmware (Bosch, Continental, KPIT, Tata Elxsi, L&T Technology Services, Wipro Engineering Edge): writing I2C/SPI/CAN drivers, ISRs and DMA setup on ARM Cortex-M/R parts for ECUs, clusters and ADAS - interrupt latency and ring buffers are daily work.
- Mobile and SoC teams (Qualcomm Bengaluru/Hyderabad, Samsung R&D, MediaTek, Intel, NVIDIA, Texas Instruments): camera, display, modem and sensor drivers in the Android/Linux kernel; MSI-X, IOMMU/SMMU and scatter-gather DMA for high-rate data paths.
- Storage and networking products (NetApp, Dell EMC, Cisco, Arista, Nutanix, Juniper India): NVMe and NIC drivers, interrupt coalescing, zero-copy and polling data planes (DPDK/SPDK) where interrupt cost would otherwise cap throughput.
- Server and cloud operations (TCS, Infosys, Wipro, HCLTech, Accenture; any Indian bank or telecom data centre): diagnosing I/O-bound systems with
iostat,vmstat,iotop,dmesg,/proc/interrupts; tuning queue depth, buffer sizes andfsyncbehaviour for Oracle/Postgres workloads and UPI/IMPS transaction systems. - Defence, space and railways (ISRO, DRDO, BEL, HAL, RDSO suppliers): RTOS-based systems (VxWorks, FreeRTOS, RTEMS) where worst-case interrupt latency is a specified, tested requirement.
- Industrial automation and IoT (Siemens, Honeywell, Rockwell, Indian smart-meter and PoS vendors): polled Modbus/serial links, printer spooling, and UART/USB device enumeration; hardware sold in India also needs BIS/CRS, TEC-MTCTE or WPC clearance as applicable.
- Job roles: embedded/firmware engineer, Linux device-driver developer, BSP engineer, kernel engineer, SRE/system administrator, validation and test engineer.
Interview questions
- Q: What is DMA and why is it used? A: DMA is a controller that transfers a block of data directly between a device and memory after the CPU programs the address and count, raising just one interrupt at the end; it frees the CPU from moving every word and so cuts CPU overhead by orders of magnitude for large transfers.
- Q: Polling versus interrupts - when would you still choose polling? A: When the device is so fast or so busy that the interrupt overhead exceeds the wait (high-rate NICs with DPDK, short status checks), in boot/firmware code with no interrupt infrastructure, or when deterministic timing matters more than CPU efficiency.
- Q: What happens, step by step, when an interrupt arrives? A: The CPU finishes the current instruction, saves PC and PSW, masks equal/lower-priority interrupts, fetches the handler address from the interrupt vector table, runs the ISR, acknowledges the device, restores the saved state and resumes with
IRET. - Q: Difference between memory-mapped and port-mapped I/O? A: Memory-mapped I/O assigns device registers ordinary memory addresses so normal load/store instructions work; port-mapped I/O uses a separate I/O address space with special
IN/OUTinstructions. Memory-mapped is simpler and handles large regions but consumes address space. - Q: Buffer or cache - what is the difference? A: A buffer is a staging area that may hold the only copy of data, used to bridge speed and size mismatches; a cache always holds a duplicate of data that exists elsewhere, kept only to make repeat access faster.
- Q: Why is spooling needed? A: A printer or tape cannot interleave output from several processes, so each job is written to a spool file and the spooler serves them one at a time - the non-shareable device then behaves like a shareable virtual device.
- Q: What is the difference between non-blocking and asynchronous I/O? A: A non-blocking call returns immediately with whatever data is available right now (possibly none); an asynchronous call returns immediately with no data and completes the full transfer in the background, notifying the process later.
- Q: Why are I/O instructions privileged? A: So user processes cannot program device controllers directly and bypass file permissions, corrupt other processes' data or crash the hardware; all access goes through system calls that the kernel validates.
- Q: What are major and minor device numbers in Linux? A: The major number identifies the driver handling the device, the minor number identifies which particular unit or partition that driver should act on; both are shown in
ls -l /dev. - Q: Why are ISRs kept as short as possible, and what is the bottom half? A: Long ISRs run with interrupts disabled and raise interrupt latency, risking missed events; the top half does only the urgent part and defers the rest to a softirq, tasklet or workqueue (the bottom half) that runs with interrupts enabled.
- Q: What problem does DMA create with caches, and how is it solved? A: DMA writes memory without the CPU's knowledge, so cached copies become stale; it is solved by hardware cache snooping or by the driver invalidating/flushing the relevant cache lines before and after the transfer.
- Q: What does a device driver provide to the kernel? A: A uniform set of entry points (
open,read,write,ioctl,release,poll, plus an interrupt handler) that hide the device's register-level details, so the device-independent kernel I/O subsystem can treat all devices the same way. - Q: Explain cycle stealing in DMA. A: The DMA controller acquires the bus for one word at a time and releases it, interleaving with CPU accesses; the CPU is slowed by the fraction of bus cycles stolen rather than being halted for the whole block, as in burst mode.
Common mistakes
- Saying DMA "does not use the CPU at all" - the CPU still programs the controller, handles the completion interrupt and competes for the bus and memory.
- Confusing buffering with caching, or calling a print queue a buffer; spooling, buffering and caching solve three different problems.
- Thinking an interrupt is the same as a system call - a system call is a deliberate software trap from user code, an interrupt is an asynchronous external event.
- Counting one interrupt per block in interrupt-driven I/O numericals (it is one per byte or word; one per block is DMA).
- Treating non-blocking and asynchronous I/O as synonyms, and forgetting that the buffer in an async call must not be touched until completion.
- Forgetting
volatile(and the no-cache requirement) on memory-mapped device registers, then being puzzled when the compiler optimises away a status-bit poll loop. - Mixing units in rate calculations - bits with bytes, MB with MiB, cycles with seconds - and forgetting to divide a device rate by the transfer size to get interrupts per second.
- Putting long work, sleeping calls or locks held for a long time inside an ISR.
Related topics
- File systems and disk scheduling · Processes and threads · Memory management and virtual memory · Linux internals: processes, signals and permissions · Computer organisation: bus structure and interrupts · Embedded systems and microcontrollers.