select(2)

select(2) โ€” fresh outta the System Calls Manual, bestie ๐Ÿ“šโœจ like section 2, we're in the deep end and I'm holding ur hand the whole way ๐Ÿซถ

๐ŸŽ€ NAME ๐ŸŽ€

okay so this entire page is about one little squad of functions: select, pselect, FD_CLR, FD_ISSET, FD_SET, FD_ZERO, and the fd_set type. Together? They do synchronous I/O multiplexing. That's the whole vibe. ๐Ÿ’…

๐Ÿ“š LIBRARY ๐Ÿ“š

Standard C library (libc, so link it with -lc) ๐Ÿ’–

โœจ SYNOPSIS โœจ

#include <sys/select.h>

typedef /* ... */ fd_set;

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

void FD_CLR(int fd, fd_set *set);
int  FD_ISSET(int fd, const fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);

int pselect(int nfds, fd_set *_Nullable restrict readfds,
           fd_set *_Nullable restrict writefds,
           fd_set *_Nullable restrict exceptfds,
           const struct timespec *_Nullable restrict timeout,
           const sigset_t *_Nullable restrict sigmask);

๐ŸŒธ Feature Test Macro Requirements ๐ŸŒธ (for glibc โ€” peep feature_test_macros(7) for the full tea)

  • pselect(): _POSIX_C_SOURCE >= 200112L

๐Ÿฆ‹ DESCRIPTION ๐Ÿฆ‹

โš ๏ธ WARNING. Sit down. This one's important ๐Ÿ’€ select() can only monitor file descriptors with numbers less than FD_SETSIZE (1024) โ€” which is, lowkey, an unreasonably low limit for a lot of modern applications ๐Ÿ˜ญ โ€” and bestie, this limitation will not change. Ever. Periodt. Every modern application should be reaching for poll(2) or epoll(7) instead, because those two do not suffer from this limitation. no cap.

okay so select() lets a program monitor multiple file descriptors at once, just vibing, waiting until one or more of them become "ready" for some class of I/O operation (like, input is possible, yk the drill). โœจ A file descriptor counts as ready if it's possible to perform a corresponding I/O operation โ€” think read(2), or a sufficiently small write(2) โ€” without blocking. that's it, that's the definition ๐Ÿซถ

๐Ÿงธ fd_set ๐Ÿงธ

It's a structure type that can represent a whole set of file descriptors. Per POSIX, the maximum number of file descriptors that can live inside an fd_set structure is whatever the macro FD_SETSIZE says it is. iykyk ๐ŸŒธ

๐Ÿ’… File descriptor sets ๐Ÿ’…

The main character arguments of select() are three "sets" of file descriptors (declared with the type fd_set), and they let the caller wait for three different classes of events on a specified set of file descriptors. And here's the cute part: each of the fd_set arguments can be passed as NULL if you don't want to watch anything for that particular class of events. flexible! ๐ŸŒˆ

๐Ÿšจ Note well, this one bites people fr fr: upon return, each of the file descriptor sets gets modified in place to show which file descriptors are currently "ready". So if you're using select() inside a loop? You must reinitialize the sets before every single call. no skipping. ๐Ÿ™ˆ

You manipulate the contents of a file descriptor set with these macros:

  • FD_ZERO() โ€” clears the set (removes all file descriptors from it). This should be your literal first step when initializing a file descriptor set. start here bestie ๐Ÿ’–
  • FD_SET() โ€” adds the file descriptor fd to set. And if you add one that's already in there? Total no-op, no error, we don't gatekeep. โœจ
  • FD_CLR() โ€” removes the file descriptor fd from set. Removing one that isn't in there? Also a no-op, also no error. iconic. ๐ŸŽ€
  • FD_ISSET() โ€” see, select() rewrites the contents of the sets according to the rules below. So after calling select(), you use the FD_ISSET() macro to test whether a file descriptor is still present in a set. It returns nonzero if fd is in set, and zero if it's not. ๐ŸŒŸ

๐Ÿ—ฃ๏ธ Arguments ๐Ÿ—ฃ๏ธ

Here's the rundown on what you actually pass to select():

  • readfds โ€” the file descriptors in this set get watched to see if they're ready for reading. A fd is ready for reading if a read operation will not block; and specifically, a fd is also ready on end-of-file. After select() returns, readfds gets cleared of everything except the ones that are ready for reading. ๐Ÿ‘‘
  • writefds โ€” these get watched to see if they're ready for writing. A fd is ready for writing if a write operation will not block. However โ€” and this is important โ€” even if a file descriptor says it's writable, a large write may still block. ๐Ÿ™ˆ After select() returns, writefds gets cleared of everything except the ones ready for writing.
  • exceptfds โ€” these get watched for "exceptional conditions". For some examples of what counts as an exceptional condition, go look at the discussion of POLLPRI in poll(2). After select() returns, exceptfds gets cleared of everything except the ones where an exceptional condition actually occurred. ๐Ÿฆ‹
  • nfds โ€” set this to the highest-numbered file descriptor in any of the three sets, plus 1. The indicated file descriptors in each set get checked, up to this limit (but see BUGS ๐Ÿ‘€).
  • timeout โ€” this one's a timeval structure (shown below) that specifies how long select() should block waiting for a file descriptor to become ready. The call will block until either:
    • a file descriptor becomes ready;
    • the call is interrupted by a signal handler; or
    • the timeout expires.

Note that the timeout interval gets rounded up to the system clock granularity, and kernel scheduling delays mean the blocking interval may overrun by a small amount. ngl, it's not that precise. โฐ

If both fields of the timeval structure are zero, then select() returns immediately. (Super useful for polling! ๐Ÿ”ฅ)

And if timeout is specified as NULL, select() blocks indefinitely waiting for a file descriptor to become ready. main character energy โ€” it will wait forever if it has to. ๐Ÿ’œ

๐Ÿ”ฎ pselect() ๐Ÿ”ฎ

The pselect() system call lets an application safely wait until either a file descriptor becomes ready or a signal is caught. ๐ŸŒธ

The operation of select() and pselect() is identical, except for these three differences:

  • select() uses a timeout that's a struct timeval (seconds and microseconds), while pselect() uses a struct timespec (seconds and nanoseconds). โฑ๏ธ
  • select() may update the timeout argument to show how much time was left. pselect() does not change this argument.
  • select() has no sigmask argument, and behaves exactly like pselect() called with a NULL sigmask. ๐Ÿ’€

sigmask is a pointer to a signal mask (see sigprocmask(2)); if it's not NULL, then pselect() first replaces the current signal mask with the one sigmask points to, then does the "select" thing, and then restores the original signal mask. (If sigmask is NULL, the signal mask isn't touched at all during the pselect() call.) ๐Ÿซถ

Other than the difference in the precision of the timeout argument, this pselect() call:

ready = pselect(nfds, &readfds, &writefds, &exceptfds,
                timeout, &sigmask);

is equivalent to atomically executing these calls:

sigset_t origmask;

pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
ready = select(nfds, &readfds, &writefds, &exceptfds, timeout);
pthread_sigmask(SIG_SETMASK, &origmask, NULL);

So why does pselect() even need to exist? Because if you want to wait for either a signal or a file descriptor to become ready, you need an atomic test to prevent race conditions. Here's the scenario bestie: suppose the signal handler sets a global flag and returns. Then a test of that global flag, followed by a call to select(), could hang indefinitely if the signal arrived just after the test but just before the call. ๐Ÿ˜ญ By contrast, pselect() lets you first block signals, handle the signals that have come in, then call pselect() with the desired sigmask โ€” which avoids the race entirely. slay. โœจ

โณ The timeout โณ

The timeout argument for select() is a structure of this type:

struct timeval {
    time_t      tv_sec;         /* seconds */
    suseconds_t tv_usec;        /* microseconds */
};

The corresponding argument for pselect() is a timespec(3) structure. ๐ŸŒ™

On Linux, select() modifies timeout to reflect the amount of time not slept; most other implementations do not do this. (POSIX.1 permits either behavior, so nobody's in trouble ๐Ÿ’….) And this causes problems both when Linux code that reads timeout gets ported to other operating systems, and when code gets ported to Linux that reuses a struct timeval for multiple select()s in a loop without reinitializing it. So, real talk: consider timeout to be undefined after select() returns. ๐Ÿ“ข


๐ŸŽ RETURN VALUE ๐ŸŽ

On success, select() and pselect() return the number of file descriptors contained in the three returned descriptor sets โ€” that is, the total number of bits that are set in readfds, writefds, exceptfds. The return value may be zero if the timeout expired before any file descriptors became ready. (zero is still a win, it's not an error, don't panic ๐Ÿซถ)

On error, -1 is returned, and errno is set to indicate the error; the file descriptor sets are unmodified, and timeout becomes undefined. ๐Ÿ˜ญ

๐Ÿ’€ ERRORS ๐Ÿ’€

  • EBADF โ€” an invalid file descriptor was given in one of the sets. (Maybe a fd that was already closed, or one where an error has occurred.) However, see BUGS. ๐Ÿ‘€
  • EINTR โ€” a signal was caught; see signal(7). โšก
  • EINVAL โ€” nfds is negative, or it exceeds the RLIMIT_NOFILE resource limit (see getrlimit(2)).
  • EINVAL โ€” the value contained within timeout is invalid. ๐Ÿ™ˆ
  • ENOMEM โ€” unable to allocate memory for internal tables. ๐Ÿ˜ญ

๐ŸŒˆ VERSIONS ๐ŸŒˆ

On some other UNIX systems, select() can fail with the error EAGAIN if the system fails to allocate kernel-internal resources โ€” rather than ENOMEM like Linux does. POSIX specifies this error for poll(2), but not for select(). So if you're writing portable programs, you might wanna check for EAGAIN and loop, just like you'd do with EINTR. iykyk ๐Ÿ’œ

๐Ÿ“ STANDARDS ๐Ÿ“

POSIX.1-2024. certified. ๐Ÿ‘‘

๐Ÿ“œ HISTORY ๐Ÿ“œ

  • select()
    • POSIX.1-2001, 4.4BSD (first appeared in 4.2BSD). ๐ŸŒฑ
    • Generally portable to/from non-BSD systems supporting clones of the BSD socket layer (including System V variants). However, note that the System V variant typically sets the timeout variable before returning, but the BSD variant does not. ๐Ÿ™ˆ
  • pselect()
    • Linux 2.6.16. POSIX.1g, POSIX.1-2001. ๐ŸŽ€
    • Prior to this, it was emulated in glibc (but see BUGS ๐Ÿ‘€).
  • fd_set โ€” POSIX.1-2001. โœจ

โœจ NOTES โœจ

Okay bestie, notes time ๐Ÿ’… The header <sys/time.h> also gives you the fd_set type. Two-for-one, slay.

An fd_set is a fixed size buffer. If you run FD_CLR() or FD_SET() with an fd that is negative or is equal to or larger than FD_SETSIZE, that's undefined behavior โ€” literally do not. Also, POSIX says fd has to be a valid file descriptor. And the O_NONBLOCK flag does not affect how select() and pselect() work. Periodt.

๐Ÿงต The self-pipe trick

On systems that don't have pselect(), you can still trap signals reliably (and more portably) with the self-pipe trick. In this technique, a signal handler writes a byte to a pipe, and the other end of that pipe is monitored by select() in the main program. To avoid possibly blocking when writing to a pipe that may be full or reading from a pipe that may be empty, nonblocking I/O is used when reading from and writing to the pipe. No cap.

โฐ Emulating usleep(3)

Before usleep(3) showed up, some code used to call select() with all three sets empty, nfds zero, and a non-NULL timeout as a fairly portable way to sleep with subsecond precision. Iconic workaround behavior tbh.

๐Ÿชž Correspondence between select() and poll() notifications

Inside the Linux kernel source, there are these definitions that show how the readable, writable, and exceptional condition notifications of select() line up with the event notifications from poll(2) and epoll(7):

           #define POLLIN_SET  (EPOLLRDNORM | EPOLLRDBAND | EPOLLIN |
                                EPOLLHUP | EPOLLERR)
                              /* Ready for reading */
           #define POLLOUT_SET (EPOLLWRBAND | EPOLLWRNORM | EPOLLOUT |
                                EPOLLERR)
                              /* Ready for writing */
           #define POLLEX_SET  (EPOLLPRI)
                              /* Exceptional condition */

๐Ÿ’€ Multithreaded applications

If a file descriptor being monitored by select() is closed in another thread, the result is unspecified. On some UNIX systems, select() unblocks and returns, with an indication that the file descriptor is ready โ€” a subsequent I/O operation will likely fail with an error, unless another process reopens the file descriptor between the time select() returned and the I/O operation is performed. On Linux (and some other systems), closing the file descriptor in another thread has no effect on select(). In summary: any application that relies on a particular behavior in this scenario must be considered buggy. Fr fr.

๐Ÿงš C library/kernel differences

The Linux kernel allows file descriptor sets of arbitrary size, determining the length of the sets to be checked from the value of nfds. But in the glibc implementation, the fd_set type is fixed in size. See also BUGS below, bestie.

The pselect() interface described in this page is implemented by glibc. The underlying Linux system call is named pselect6(). This system call has somewhat different behavior from the glibc wrapper function.

The Linux pselect6() system call modifies its timeout argument. However, the glibc wrapper function hides this behavior by using a local variable for the timeout argument that is passed to the system call. So the glibc pselect() function does not modify its timeout argument; this is the behavior required by POSIX.1-2001.

The final argument of the pselect6() system call is not a sigset_t * pointer, but is instead a structure of the form:

           struct {
               const kernel_sigset_t *ss;   /* Pointer to signal set */
               size_t ss_len;               /* Size (in bytes) of object
                                               pointed to by 'ss' */
           };

This allows the system call to obtain both a pointer to the signal set and its size, while allowing for the fact that most architectures support a maximum of 6 arguments to a system call. See sigprocmask(2) for a discussion of the difference between the kernel and libc notion of the signal set.

๐Ÿ“œ Historical glibc details

glibc 2.0 provided an incorrect version of pselect() that did not take a sigmask argument.

From glibc 2.1 to glibc 2.2.1, one must define _GNU_SOURCE in order to obtain the declaration of pselect() from <sys/select.h>.


๐Ÿฉธ BUGS ๐Ÿฉธ

POSIX allows an implementation to define an upper limit, advertised via the constant FD_SETSIZE, on the range of file descriptors that can be specified in a file descriptor set. The Linux kernel imposes no fixed limit, but the glibc implementation makes fd_set a fixed-size type, with FD_SETSIZE defined as 1024, and the FD_*() macros operating according to that limit. To monitor file descriptors greater than 1023, use poll(2) or epoll(7) instead. Sorry bestie, that's just how it is.

The implementation of the fd_set arguments as value-result arguments is a design error that is avoided in poll(2) and epoll(7). We don't love that.

According to POSIX, select() should check all specified file descriptors in the three file descriptor sets, up to the limit nfds-1. However, the current implementation ignores any file descriptor in these sets that is greater than the maximum file descriptor number that the process currently has open. According to POSIX, any such file descriptor that is specified in one of the sets should result in the error EBADF.

Starting with glibc 2.1, glibc provided an emulation of pselect() that was implemented using sigprocmask(2) and select(). This implementation remained vulnerable to the very race condition that pselect() was designed to prevent. Modern versions of glibc use the (race-free) pselect() system call on kernels where it is provided.

On Linux, select() may report a socket file descriptor as "ready for reading", while nevertheless a subsequent read blocks. This could for example happen when data has arrived but upon examination has the wrong checksum and is discarded. There may be other circumstances in which a file descriptor is spuriously reported as ready. Thus it may be safer to use O_NONBLOCK on sockets that should not block. ๐Ÿ’…

On Linux, select() also modifies timeout if the call is interrupted by a signal handler (i.e., the EINTR error return). This is not permitted by POSIX.1. The Linux pselect() system call has the same behavior, but the glibc wrapper hides this behavior by internally copying the timeout to a local variable and passing that variable to the system call.


โœจ EXAMPLES โœจ

Here's the whole vibe in C:

       #include <stdio.h>
       #include <stdlib.h>
       #include <sys/select.h>
       #include <sys/time.h>

       int
       main(void)
       {
           int             retval;
           fd_set          rfds;
           struct timeval  tv;

           /* Watch stdin (fd 0) to see when it has input.  */

           FD_ZERO(&rfds);
           FD_SET(0, &rfds);

           /* Wait up to five seconds.  */

           tv.tv_sec = 5;
           tv.tv_usec = 0;

           retval = select(1, &rfds, NULL, NULL, &tv);
           /* Don't rely on the value of tv now! */

           if (retval == -1)
               perror("select()");
           else if (retval)
               printf("Data is available now.\n");
               /* FD_ISSET(0, &rfds) will be true.  */
           else
               printf("No data within five seconds.\n");

           exit(EXIT_SUCCESS);
       }

๐Ÿ‘€ SEE ALSO ๐Ÿ‘€

For a tutorial with discussion and examples, see select_tut(2). ๐Ÿซถ


This page is brought to you by Linux man-pages 6.20, dated 2026-02-08, and it's the select(2) manual page. Go off, bestie ๐Ÿ’–โœจ

๐Ÿ“ฆ where this page comes from
  • arch rolling: man-pages 6.20-1 ยท BSD-2-Clause AND BSD-3-Clause AND BSD-4.3TAHOE AND BSD-4-Clause-UC AND GPL-1.0-or-later AND GPL-2.0-only AND GPL-2.0-or-later AND GPL-3.0-or-later AND LGPL-3.0-linking-exception AND LGPL-3.0-or-later AND LicenseRef-Public-Domain AND LicenseRef-UltraPermissive AND Linux-man-pages-1-para AND Linux-man-pages-copyleft AND Linux-man-pages-copyleft-2-para AND Linux-man-pages-copyleft-var AND MIT AND Spencer-94
  • nixpkgs unstable: man-pages 6.20
  • ๐Ÿ“œ the page's license: Linux-man-pages-copyleft
  • ๐Ÿ’– girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms