Blocking vs non-blocking I/O: the fundamental split
When a thread calls read() on a socket, two things can happen:
- Blocking I/O (default): the kernel suspends the thread until data arrives. The thread consumes a stack (~1–8 MB) and an OS scheduling slot while doing nothing.
- Non-blocking I/O: the call returns immediately — either with data or with
EAGAIN("no data yet, try again"). The thread is free to do other work.
With blocking I/O, the standard model is one thread per connection. At 10 000 concurrent connections you have 10 000 threads, each burning stack memory and causing 10 000-way context-switching. This is the C10K problem (Kegel, 1999).
Non-blocking I/O breaks the 1:1 mapping: a single thread can manage thousands of connections by checking which ones have data ready and processing only those. The mechanism that tells you "these file descriptors are ready" is select, poll, epoll (Linux), or kqueue (BSD/macOS).
