Backend Development 7 min read

Understanding Epoll and Non‑Blocking I/O Through a Process Story

This article narrates how a process interacts with sockets, the drawbacks of blocking reads, the shift to non‑blocking I/O, and the advantages of epoll’s multiplexing in Linux, illustrating the concepts with a humorous story and visual diagrams.

Refining Core Development Skills
Refining Core Development Skills
Refining Core Development Skills
Understanding Epoll and Non‑Blocking I/O Through a Process Story

Hello everyone, I'm Fei! Today I bring you a comic‑style story about processes and network I/O.

I'm a process named "little P", created and managed by the operating system like many of my peers.

We run in user space while the kernel runs in kernel space, and we have no direct access to devices such as disks or NICs; we must use system calls to enter kernel mode, which perform strict security checks.

Now I’ll explain how we handle network I/O.

01

Processes communicate with users through sockets, but the actual sockets and all network packets are controlled by the kernel; we only see a socket descriptor.

In the early days we handled a single TCP connection using the recvfrom system call, hoping the data would be ready when we called it.

When no data was available, we had to yield the CPU, which was acceptable when only one connection existed.

Later, the workload increased to hundreds or thousands of connections. Blocking on a read without data caused severe inefficiency because we had to save state and lost cache locality.

We asked the OS to make sockets non‑blocking, and it agreed. This allowed us to poll each socket in a loop without being blocked.

However, we still didn’t know when data would arrive, so we kept repeatedly querying the kernel, which was wasteful.

02

To avoid endless polling, the kernel introduced multiplexing system calls: select , poll , and epoll .

Among them, epoll became my favorite because it internally uses a red‑black tree to maintain the set of sockets I’m interested in.

Now, instead of constantly looping, I can ask the kernel for the ready queue; if a socket has data, the kernel tells me immediately.

This dramatically improves efficiency: I can handle many concurrent connections, achieving up to 100,000 QPS in Redis tests.

When no sockets have data, I block voluntarily, freeing the CPU until a soft interrupt wakes me up via the epoll wait queue.

The wake‑up only places me back into the ready queue; the scheduler later gives me CPU time.

03

Understanding kernel‑level mechanisms like epoll greatly boosts one’s ability to write high‑performance network services.

If you enjoyed this story, please like, view, and share, and feel free to join my technical group or grab my e‑book “Understanding Implementation and Network Performance”.

GitHub: https://github.com/yanfeizhang/coder-kung-fu

ProcessLinux kernelnetwork programmingepollsocketNon-blocking I/O
Refining Core Development Skills
Written by

Refining Core Development Skills

Fei has over 10 years of development experience at Tencent and Sogou. Through this account, he shares his deep insights on performance.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.