Compare

Side-by-side on the decisions that recur: process vs thread, threads vs async, mutex vs semaphore, blocking vs non-blocking I/O, container vs VM — with when to choose each.

select / pollepoll / kqueue
InterfacePass the whole set of descriptors on every callRegister once, then wait — the kernel keeps the interest list (Linux epoll, BSD/macOS kqueue; Windows uses IOCP completions instead)
Cost per callO(n) to copy and scan all descriptors, ready or notO(ready) — only descriptors with events are returned
Descriptor limitselect: FD_SETSIZE (1024) and a bitmap; poll: no hard limit but still O(n)Limited only by ulimit -n; tens of thousands is routine
Trigger modeLevel-triggered onlyLevel- or edge-triggered (EPOLLET, EV_CLEAR); edge mode needs you to drain until EAGAIN
PortabilityPOSIX everywhere, including Windows for socketsPlatform-specific; libraries (libuv, asio, Tokio, Java NIO) hide the difference
Who uses itSmall tools, portable code with a handful of descriptorsNode.js, nginx, Redis, Envoy, every 10k-connection server
Choose this whenA handful of descriptors, maximum portability, or a quick tool where O(n) per call is irrelevant.Thousands of connections per thread, or any server whose descriptor count grows with users — the C10K answer.