From man select

int select(int nfds, fd_set *readfds, fd_set *writefds,
           fd_set *exceptfds, struct timeval *timeout);

nfds is the highest-numbered file descriptor in any of the three sets, plus 1.

What is the purpose of nfds, when we already have readfds, writefds and exceptfds, from which the file descriptors can be determined?


In "Advanced Programming in the UNIX Environment", W. Richard Stevens says it is a performance optimization:

By specifying the highest descriptor we're interested in, the kernel can avoid going through hundred of unused bits in the three descriptor sets, looking for bits that are turned on.

(1st edition, page 399)

If you are doing any kind of UNIX systems programming, the APUE book is highly recommended.


An fd_set is usually able to track up to 1024 file descriptors.

The most efficient way to track which fds are set to 0 and which are set to 1 would be a bitset, so each fd_set would consist of 1024 bits.

On a 32-bit system, a long int (or "word") is 32 bits, so that means each fd_set is
1024 / 32 = 32 words.

If nfds is something small, such as 8 or 16, which it would be in many applications, it only needs to look inside the 1st word, which should clearly be faster than looking inside all 32.

(See FD_SETSIZE and __NFDBITS from /usr/include/sys/select.h for the values on your platform.)


As to why the function signature isn't

int select(fd_set *readfds, int nreadfds,
           fd_set *writefds, int nwritefds,
           fd_set *exceptfds, int nexceptfds,
           struct timeval *timeout);

My guess is it's because the code tries to keep all the arguments in registers, so the CPU can work on them faster, and if it had to track an extra 2 variables, the CPU might not have enough registers.

So in other words, select is exposing an implementation detail so that it can be faster.

| improve this answer | |

I don't know for sure, since I'm not one of the designers of select(), but I'd say it's a performance optimization. The calling function knows how many file descriptors it put in the read, write and except FDs, so why should the kernel figure it out again?

Remember that in the early 80s, when select() got introduced, they didn't have multi-gigaghertz, multi-processors to work with. A 25 MHz VAX was pretty doggone fast. Plus, you wanted select() to work fast if it could: if some I/O was waiting for the process, why make the process wait?

| improve this answer | |
  • To your argument I would say we need nreadfds, nwritefds and nexceptfds instead of just one nfds. – phunehehe Feb 21 '11 at 5:46
  • Maybe it's so that nfds can go in a register for faster access. If it had to track three numbers, along with all the other arguments, maybe the CPU would not have enough registers. Of course, the kernel could have created its own nfds based on your hypothetical 3 variables. So my guess is it's exposing an implementation detail to gain efficiency. – Mikel Feb 21 '11 at 21:29
  • @Mikel, phunehehe: Separate nfds arguments would bring very little gain. Most of the times, the process has opened very few processes relative to FD_SETSIZE. A typical case might have (4,4,2) out of 1024; making the kernel check (4,4,4) is a big win over (1024,1024,1024), but optimizing down to (4,4,2) would be near-useless. – Gilles 'SO- stop being evil' Feb 21 '11 at 21:56
  • @Gilles: the gain would be a cleaner API. (As it is, either the programmer has to do the extra work to calculate nfds, or be lazy and call select(FD_SETSIZE, ...), which would be slower.) – Mikel Feb 21 '11 at 22:38
  • OTOH, tracking only one max variable could be easier for the programmer too. – Mikel Feb 21 '11 at 22:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.