Understanding IO Models: Blocking, Non‑Blocking, Multiplexing, Signal‑Driven and Asynchronous IO
This article explains the concept of IO, the role of the operating system, the complete IO workflow, and compares five common IO models—blocking, non‑blocking, select/poll/epoll multiplexing, signal‑driven, and asynchronous—highlighting their mechanisms, advantages, and typical use cases.
IO (Input/Output) refers to the data exchange between an application and external devices such as disks or networks, and it is mediated by the operating system through system calls.
An application cannot directly perform IO; it must request the OS via APIs, which then operates in two phases: an IO call from the process to the kernel, and the IO execution performed by the kernel.
The full IO process includes data preparation (kernel waits for the device and copies data to a kernel buffer) and data copy (kernel copies data to the user buffer). The overall flow can be visualized as a sequence of steps from request to completion.
1. Blocking IO – the server thread blocks on accept() and read() . Example pseudocode:
listenfd = socket();
bind(listenfd);
listen(listenfd);
while (true) {
buf = new buf[1024];
connfd = accept(listenfd); // block until connection
int n = read(connfd, buf); // block until data
doSomeThing(buf);
close(connfd);
}2. Non‑Blocking IO – the read call returns immediately with -1 if data is not ready, requiring the application to poll repeatedly, which can waste CPU cycles.
arr = new Arr[];
listenfd = socket();
bind(listenfd);
listen(listenfd);
while (true) {
connfd = accept(listenfd); // block for connection
arr.add(connfd);
}
// In another thread
for (connfd : arr) {
buf = new buf[1024];
int n = read(connfd, buf); // non‑blocking read
if (n != -1) {
newThreadDeal(buf);
close(connfd);
arr.remove(connfd);
}
}3. IO Multiplexing (select, poll, epoll) – allows a single thread to monitor many file descriptors. select uses a fixed‑size bitmap (max 1024 fds), poll uses a dynamic array, and epoll employs a red‑black tree and ready‑queue to avoid repeated copying and scanning.
Key epoll operations:
epoll_create() – creates an epoll instance.
epoll_ctl() – adds, removes, or modifies monitored fds.
epoll_wait() – blocks until one or more fds become ready.
epoll supports two trigger modes:
LT (Level‑Triggered) – events are reported as long as the condition holds.
ET (Edge‑Triggered) – events are reported only when the state changes, requiring the application to drain the socket buffer in one go.
4. Signal‑Driven IO – the kernel sends a SIGIO signal to the process when data is ready, allowing the thread to continue without blocking.
5. Asynchronous IO – the application issues a single read request and the kernel notifies it when the operation completes, eliminating the need for separate readiness checks and data‑copy phases.
Overall, these models evolve from simple blocking calls to sophisticated event‑driven mechanisms that reduce system‑call overhead, improve scalability, and better utilize CPU resources.
JD Retail Technology
Official platform of JD Retail Technology, delivering insightful R&D news and a deep look into the lives and work of technologists.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.