I’m not convinced. Can anybody explain what’s the set of desirable properties of polling interfaces, and why we need at least four different interfaces to implement all of them?
I’m not convinced. Can anybody explain what’s the set of desirable properties of polling interfaces, and why we need at least four different interfaces to implement all of them?
I'll give you a partial answer.
One desirable property is that each iteration of your event loop is not O(n) with the total number of descriptors being watched. select() and poll() are flawed for that reason—the entire list of file descriptors is passed in on each iteration and has to at least be compared to the previous iteration. No one writes things using these ancient interfaces anymore. (That's a bit unfortunate given that all the modern interfaces are single-platform, so everyone who cares about portability needs an abstraction layer, but it is what it is.) epoll() is better.
The kernel doesn't break old programs, so old interfaces stick around, basically no matter how bad they are. There are three interfaces now, so there have to be at least three. If there can be only three or if there have to be four comes down to if epoll is (or can become) good enough or not. If folks keep coming up with new requirements that can't be met with existing interfaces, there will just be more and more interfaces over time...
If those old ones don’t have any unique properties, couldn’t they be implemented on top of a single syscall, or is the syscall interface sacred on Linux, and calls cannot be retired?
Bad property 1. Limited number of things you can block on. select(2) can only do FD_SETSIZE descriptors.
Bad property 2. Entire list of descriptors needs to be re-examined on every blocking call. select(2) and poll(2) have this problem. Entering the syscall, the kernel needs to copy the list of descriptors, and after returning from it, the application needs to scan the list to see what changed - these are both O(N) operations. (Aside: Windows's WaitForMultipleObjects suffers from the former problem but not the latter, but has bad property #1 since it's limited to 64 handles.) epoll and kqueue fix this by having the kernel maintain the list of descriptors across calls, notifying you of which ones change.
Bad property 3. Too many syscalls, which epoll suffers from and this latest one is trying to solve. kqueue(2) tries to mitigate this by allowing you to batch add/remove descriptors, rather than doing a separate syscall for each.