Why Blocking I/O Slows Your Server and How Select, Poll, and Epoll Fix It
This article explains the drawbacks of traditional blocking network I/O, introduces non‑blocking reads, and walks through the evolution from multithreaded workarounds to kernel‑level multiplexing mechanisms such as select, poll, and epoll, showing code examples and performance considerations.
Traditional blocking I/O makes a server thread wait in two places: the accept call for new connections and the read call for incoming data. If a client stops sending data, the thread remains blocked and cannot serve other clients.
Blocking I/O Example
listenfd = socket();
bind(listenfd);
listen(listenfd);
while (1) {
connfd = accept(listenfd); // blocks until a client connects
int n = read(connfd, buf); // blocks until data arrives
doSomeThing(buf);
close(connfd);
}When read blocks, the server cannot accept new connections, leading to resource starvation.
Naïve Non‑Blocking Attempt with Threads
One common trick is to spawn a new thread for each read operation, so the main thread can continue accepting connections. The OS still provides a blocking read, so this only moves the blockage to another thread.
while (1) {
connfd = accept(listenfd); // blocks
pthread_create(doWork);
}
void doWork() {
int n = read(connfd, buf); // still blocks
doSomeThing(buf);
close(connfd);
}A true non‑blocking read is obtained by setting the file descriptor to O_NONBLOCK before calling read. The call returns -1 immediately if no data is available.
fcntl(connfd, F_SETFL, O_NONBLOCK);
int n = read(connfd, buffer); // returns -1 if no dataI/O Multiplexing with select
The select system call lets the kernel examine a set of file descriptors and report which ones are ready for I/O, eliminating the need for a thread per connection.
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);Typical usage:
// Thread 1: accept connections and add them to a list
while (1) {
connfd = accept(listenfd);
fcntl(connfd, F_SETFL, O_NONBLOCK);
fdlist.add(connfd);
}
// Thread 2: wait for readiness
while (1) {
int nready = select(fdlist);
for (fd in fdlist) {
if (fd != -1) {
read(fd, buf);
if (--nready == 0) break;
}
}
}Limitations of select:
Requires copying the descriptor set to kernel space, costly at high concurrency.
Performs a linear scan in the kernel, which is still synchronous.
Only returns the count of ready descriptors; the user must iterate the whole set.
poll – Removing the Descriptor Limit
polluses an array of struct pollfd and eliminates the 1024‑descriptor limit of select.
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
struct pollfd {
int fd; // file descriptor
short events; // events to monitor
short revents;// events that occurred
};epoll – Scalable Event Notification
epolladdresses all three drawbacks of select by keeping the descriptor set inside the kernel, using edge‑triggered notifications, and returning only the ready descriptors.
int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event *events,
int maxevents, int timeout);With epoll, a single system call can monitor thousands of sockets, and the kernel only notifies the application about descriptors that actually have I/O events, removing unnecessary user‑space traversal.
Summary
Blocking I/O blocks the server thread on accept and read. User‑space tricks like spawning threads or looping over non‑blocking reads work but still incur many system calls. Kernel‑level multiplexing APIs—first select, then poll, and finally epoll —provide increasingly efficient ways to monitor many descriptors with minimal overhead.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Liangxu Linux
Liangxu, a self‑taught IT professional now working as a Linux development engineer at a Fortune 500 multinational, shares extensive Linux knowledge—fundamentals, applications, tools, plus Git, databases, Raspberry Pi, etc. (Reply “Linux” to receive essential resources.)
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.
