For a Linux server monitoring a very large set of mostly idle connections, epoll is generally the best fit: it keeps watched descriptors registered in the kernel and returns ready events, instead of requiring the application to resubmit the full watched set on every wait. That design can scale better as the watched set grows, but it does not guarantee that a particular machine or application can sustain 100,000 connections.
What changes inside the wait operation?
The key difference is where the watched set lives between waits. With select and poll, the application presents the descriptors and requested events each time it waits. With epoll, the application registers descriptors with an epoll instance and later asks for events that are ready.
As an Amazon Associate I earn from qualifying purchases.
select: resubmit descriptor sets
The application supplies read, write, and exception descriptor sets plus an nfds boundary. The sets are value-result arguments: the call changes them to indicate readiness, so the caller must rebuild or restore them before the next wait. The kernel checks the specified descriptors and returns readiness information for the application to inspect.
There is also a notable limit in the glibc fd_set interface: FD_SETSIZE is 1,024, so descriptor numbers must be below 1,024 when using that interface. This is not a universal 1,024-descriptor cap imposed by the Linux kernel’s select system call; it is a constraint of the glibc interface. The Linux man-pages project describes the limitation in select(2) and says modern applications should use poll or epoll instead in this context.
#1 Best Overall
poll: resubmit an array
The application passes an array of pollfd records, each identifying a descriptor and the events of interest. Unlike glibc’s fd_set, this interface does not have that fixed 1,024-descriptor-number limit. But the application still supplies the watched array on each wait, and the kernel checks the specified descriptors to determine which are ready.
epoll: keep registrations, retrieve ready events
The application creates an epoll instance, registers or changes watched descriptors with epoll_ctl, and retrieves events with epoll_wait. The kernel maintains an interest list of registered descriptors and a ready list populated as watched descriptors become ready. The application can therefore avoid resubmitting its entire watched set on every wait.
The Linux man-pages project describes epoll as an API that “scales well to large numbers of watched file descriptors.” That is a statement about the API’s suitability for large watched sets, not a promise of a particular connection count or performance result. Michael Kerrisk’s Linux/UNIX system programming slides explain the design distinction: select and poll repeatedly pass the set to be checked, while epoll retains the watched set and makes ready events available. This describes the public interface model; it does not establish a universal complexity formula or benchmark for every kernel and workload.
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Repair Windows errors before they cause bigger problems3Fix the driver behind crashes, sound loss and screen glitchesRank #2
How do the three APIs compare for a large connection set?
| API | What the application provides | Watched-set behavior | Descriptor-number constraint | Platform scope |
|---|---|---|---|---|
select |
Read, write, and exception sets plus an nfds boundary |
Rebuild or restore the value-result sets and pass them again for each wait | glibc fd_set has FD_SETSIZE 1,024; descriptor numbers must be below 1,024 |
Longstanding portable interface |
poll |
An array of pollfd records |
Pass the watched array again for each wait | No equivalent fixed fd_set size limit |
Longstanding portable interface |
epoll |
Persistent registrations managed with epoll_ctl; ready events retrieved with epoll_wait |
Kernel retains an interest list and reports ready events | No fixed fd_set limit |
Linux-specific |
For a large set in which most connections are idle at any given moment, epoll’s persistent registration model avoids repeatedly presenting and checking the complete set in the same way as select and poll. That is the reason it is usually the suitable Linux choice here. Exact performance still depends on the workload and implementation; the design alone does not establish a fixed speedup.
Does epoll make 100,000 connections possible?
No single readiness API guarantees that capacity. The sources describing these APIs do not establish that a particular host can create or sustain 100,000 live connections, and they provide no controlled benchmark for exactly that connection count. A server’s practical capacity also depends on host and process resource limits, memory, connection state, traffic pattern, and the work the application performs per connection.
Epoll registrations themselves consume kernel memory. The Linux man-pages project gives approximate per-watch costs of about 90 bytes on a 32-bit kernel and 160 bytes on a 64-bit kernel. These are estimates for registered watches, not a total memory budget per connection: connection buffers, application state, socket memory, and other resources add to the real cost.
The same epoll(7) manual documents a default-setting rule for fs.epoll.max_user_watches: the limit is derived from one quarter (4%) of available low memory divided by the registration cost. The actual limit depends on the running host and kernel, so inspect the target system rather than assume a default. This registration limit is one constraint among many, not a connection-capacity estimate.
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →What readiness semantics should you use?
select and poll provide level-triggered readiness. Epoll is level-triggered by default, and can also be configured for edge-triggered delivery with EPOLLET. In level-triggered mode, epoll has the same readiness semantics as poll, according to the epoll manual.
Level-triggered epoll
In level-triggered mode, readiness continues to be reported while the relevant condition remains true—for example, while data remains available to read. This is generally the simpler mode to adopt when converting an event loop to epoll.
Rank #4
Edge-triggered epoll
With EPOLLET, an application must not treat an event as a standing reminder that data remains available. Use nonblocking descriptors and continue reading or writing until the operation returns EAGAIN before waiting again. If the application consumes only some buffered data and then waits for a new edge, it can block even though unread data remains.
One-shot notifications
EPOLLONESHOT disables a descriptor after an event is delivered. To receive notifications for it again, rearm it through epoll_ctl using EPOLL_CTL_MOD. This behavior can be useful when an application wants to control when a descriptor becomes eligible for another notification, but it adds explicit rearming to the event loop.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
How should an event loop handle returned events?
epoll_wait returns up to the maximum number of ready events requested by the caller. If more descriptors are ready than fit in that batch, successive calls rotate through the ready set to help avoid starvation. Each event also carries a user-data field, which the application can use to associate the notification with its own descriptor or connection state.
Best Value
A typical design registers a descriptor with the state needed by the application, waits for a bounded batch, and dispatches each returned event using that user data. In edge-triggered mode, the handler must drain the descriptor to EAGAIN; in one-shot mode, it must rearm the descriptor when appropriate. The exact event-loop structure depends on the application’s concurrency model and error-handling needs.
Which API should you choose?
- Choose epoll on Linux when monitoring a very large set, especially if most descriptors are idle much of the time and you want to retrieve only ready events.
- Choose level-triggered epoll first when you want epoll’s persistent registration model without edge-triggered drain-to-
EAGAINhandling. - Consider poll for portable code or moderate descriptor sets where resubmitting an array is acceptable and you need to avoid select’s fixed glibc
fd_setsize. - Use select only when its interface constraints fit or portability to environments that require it matters; with glibc, it cannot represent descriptor numbers 1,024 and above through
fd_set. - Check host limits before deployment, including the process’s descriptor limits and the host’s epoll watch limit; API choice alone does not size the server.
What is the practical conclusion?
For Linux applications that need to monitor a very large number of mostly idle descriptors, epoll is usually the right readiness API because registrations persist and waits return ready events rather than requiring the full watched set to be resubmitted. Poll remains a reasonable portable choice, while select’s glibc descriptor-set limit makes it unsuitable when descriptor numbers reach 1,024 or higher. None of these conclusions makes 100,000 live connections a guaranteed capacity: that must be established for the actual application and host.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




