poll(2)

๐Ÿ’… poll(2) โ€” System Calls Manual ๐Ÿ’…

okay so we're in the System Calls Manual and this page is literally about poll and ppoll, two besties who have one job: wait for some event on a file descriptor. and they do it beautifully. ๐Ÿ’–


โœจ NAME โœจ

poll, ppoll โ€” wait for some event on a file descriptor

๐Ÿ“š LIBRARY ๐Ÿ“š

Standard C library (libc, -lc). classic.


๐Ÿงš SYNOPSIS ๐Ÿงš

#include <poll.h>

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

#define _GNU_SOURCE         /* See feature_test_macros(7) */
#include <poll.h>

int ppoll(struct pollfd *fds, nfds_t nfds,
          const struct timespec *_Nullable tmo_p,
          const sigset_t *_Nullable sigmask);

๐ŸŒธ DESCRIPTION ๐ŸŒธ

so poll() is doing the same little task as select(2): it waits for one of a set of file descriptors to become ready to do I/O. and there's also the Linux-specific epoll(7) API which does a similar thing but comes with features that go beyond what poll() can do. slay for epoll honestly, but we're here for poll today. ๐Ÿ’œ

The set of file descriptors you're monitoring? That's the fds argument, and it's an array of structures that look exactly like this:

struct pollfd {
    int   fd;         /* file descriptor */
    short events;     /* requested events */
    short revents;    /* returned events */
};

The caller is the one who has to say how many items are in the fds array โ€” that goes in nfds. yes bestie, you gotta count them yourself.

The fd field holds a file descriptor for an open file. and here's a fun one: if this field is negative, then the corresponding events field gets ignored and revents returns zero. (why is that useful? because it's a super easy way to ignore a file descriptor for a single poll() call โ€” you literally just set the fd field to its bitwise complement. iconic move. โœจ)

The events field is an input parameter โ€” a bit mask saying which events the app cares about for file descriptor fd. It's allowed to be zero, and in that case the only events that can come back in revents are POLLHUP, POLLERR, and POLLNVAL (more on those below ๐Ÿ‘‡).

The revents field is an output parameter, filled in by the kernel with the events that actually happened. The bits returned in revents can include any of the ones you specified in events, plus one of POLLERR, POLLHUP, or POLLNVAL. (heads up: those three bits are meaningless in the events field, and they get set in revents whenever that condition is true, no matter what.)

If none of the requested events (and no error) has happened for any of the file descriptors, then poll() blocks until one of the events occurs. just vibing, waiting. ๐Ÿฆ‹

The timeout argument is the number of milliseconds that poll() should block waiting for a file descriptor to become ready. The call blocks until either:

  • a file descriptor becomes ready;
  • the call is interrupted by a signal handler; or
  • the timeout expires.

Being "ready" means the requested operation will not block. which is why poll()ing regular files, block devices, and other files with no reasonable polling semantic always returns instantly as ready to read and write. no waiting, no drama. ๐Ÿ’€

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. Also: a negative value in timeout means an infinite timeout. And a timeout of zero makes poll() return immediately, even if nothing is ready.

The bits that may be set/returned in events and revents are defined in <poll.h>:

  • POLLIN โ€” There is data to read.
  • POLLPRI โ€” There is some exceptional condition on the file descriptor. Possible situations include:
    • There is out-of-band data on a TCP socket (see tcp(7)).
    • A pseudoterminal master in packet mode has seen a state change on the slave (see ioctl_tty(2)).
    • A cgroup.events file has been modified (see cgroups(7)).
  • POLLOUT โ€” Writing is now possible, though a write larger than the available space in a socket or pipe will still block (unless O_NONBLOCK is set).
  • POLLRDHUP (since Linux 2.6.17) โ€” Stream socket peer closed connection, or shut down writing half of connection. The _GNU_SOURCE feature test macro must be defined (before including any header files) in order to obtain this definition.
  • POLLERR โ€” Error condition (only returned in revents; ignored in events). This bit is also set for a file descriptor referring to the write end of a pipe when the read end has been closed.
  • POLLHUP โ€” Hang up (only returned in revents; ignored in events). Note that when reading from a channel such as a pipe or a stream socket, this event merely indicates that the peer closed its end of the channel. Subsequent reads from the channel will return 0 (end of file) only after all outstanding data in the channel has been consumed.
  • POLLNVAL โ€” Invalid request: fd not open (only returned in revents; ignored in events).

When compiling with _XOPEN_SOURCE defined, you also get the following, which convey no further information beyond the bits listed above:

  • POLLRDNORM โ€” Equivalent to POLLIN.
  • POLLRDBAND โ€” Priority band data can be read (generally unused on Linux).
  • POLLWRNORM โ€” Equivalent to POLLOUT.
  • POLLWRBAND โ€” Priority data may be written.

Linux also knows about, but does not use, POLLMSG. it's just... there. lurking. ๐Ÿ™ˆ


๐ŸŽ€ ppoll() ๐ŸŽ€

The relationship between poll() and ppoll() is analogous to the relationship between select(2) and pselect(2): like pselect(2), ppoll() lets an application safely wait until either a file descriptor becomes ready or until a signal is caught. so safe, so secure, so girlboss. ๐Ÿ‘‘

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

ready = ppoll(&fds, nfds, tmo_p, &sigmask);

is nearly equivalent to atomically executing these calls:

sigset_t origmask;
int timeout;

timeout = (tmo_p == NULL) ? -1 :
          (tmo_p->tv_sec * 1000 + tmo_p->tv_nsec / 1000000);
pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
ready = poll(&fds, nfds, timeout);
pthread_sigmask(SIG_SETMASK, &origmask, NULL);

and it's only "nearly" equivalent because โ€” the way that a negative timeout value for poll() is read as an infinite timeout, but a negative value expressed in *tmo_p results in an error from ppoll(). so like, almost the same, but not quite. ๐Ÿ’…

See the description of pselect(2) for an explanation of why ppoll() is even necessary.

If the sigmask argument is specified as NULL, then no signal mask manipulation happens at all โ€” and thus ppoll() differs from poll() only in the precision of the timeout argument.

The tmo_p argument specifies an upper limit on the amount of time that ppoll() will block. This argument is a pointer to a timespec(3) structure.

If tmo_p is specified as NULL, then ppoll() can block indefinitely. forever girl. โญ


๐Ÿ’ซ RETURN VALUE ๐Ÿ’ซ

On success, poll() returns a nonnegative value which is the number of elements in the pollfds whose revents fields have been set to a nonzero value (meaning an event or an error happened). A return value of zero means the system call timed out before any file descriptors became ready.

On error, -1 is returned, and errno is set to indicate the error.


๐Ÿ˜ญ ERRORS ๐Ÿ˜ญ

  • EFAULT โ€” fds points outside the process's accessible address space. The array given as argument was not contained in the calling program's address space. ๐Ÿ’€
  • EINTR โ€” A signal occurred before any requested event; see signal(7).
  • EINVAL โ€” The nfds value exceeds the RLIMIT_NOFILE value.
  • EINVAL (ppoll()) โ€” The timeout value expressed in *tmo_p is invalid (negative).
  • ENOMEM โ€” Unable to allocate memory for kernel data structures.

๐ŸŒˆ VERSIONS ๐ŸŒˆ

On some other UNIX systems, poll() can fail with the error EAGAIN if the system fails to allocate kernel-internal resources โ€” instead of ENOMEM like Linux does. POSIX permits this behavior. so if you're writing portable programs, you may wanna check for EAGAIN and loop, just like you would with EINTR. lowkey important. ๐Ÿซถ

Some implementations define the nonstandard constant INFTIM with the value -1 for use as a timeout for poll(). This constant is not provided in glibc. no cap.

๐Ÿงต C library/kernel differences ๐Ÿงต

The Linux ppoll() system call modifies its tmo_p argument. however!! the glibc wrapper function hides this behavior by using a local variable for the timeout argument that gets passed to the system call. so the glibc ppoll() function does not modify its tmo_p argument. she keeps that to herself. ๐Ÿ’…

The raw ppoll() system call has a fifth argument, size_t sigsetsize, which specifies the size in bytes of the sigmask argument. The glibc ppoll() wrapper function specifies this argument as a fixed value (equal to sizeof(kernel_sigset_t)). See sigprocmask(2) for a discussion on the differences between the kernel and the libc notion of the sigset.


๐Ÿ“œ STANDARDS ๐Ÿ“œ

POSIX.1-2024.


๐Ÿ•ฐ๏ธ HISTORY ๐Ÿ•ฐ๏ธ

poll() โ€” POSIX.1-2001. Linux 2.1.23.

On older kernels that lack this system call, the glibc poll() wrapper function provides emulation using select(2).

ppoll() โ€” POSIX.1-2024. Linux 2.6.16, glibc 2.4.


๐Ÿ“ NOTES ๐Ÿ“

The operation of poll() and ppoll() is not affected by the O_NONBLOCK flag. wild but true.

For a discussion of what may happen if a file descriptor being monitored by poll() is closed in another thread, see select(2).


๐Ÿ› BUGS ๐Ÿ›

See the discussion of spurious readiness notifications under the BUGS section of select(2).

๐ŸŽ€ EXAMPLES ๐ŸŽ€

Okay bestie, here's the moment you've been waiting for โ€” an actual working program that uses poll(). Iconic behavior, honestly. ๐Ÿ’…

So this little program opens every file that you name on the command line, and then it watches the resulting file descriptors to see if they're ready to be read (POLLIN). It just loops forever-ish, calling poll() over and over to keep an eye on those fds, and it prints how many of them came back ready. And for each ready fd, the program does this:

  • shows you the returned revents field in a form that a human can actually read, no cap;
  • if the fd is readable, it reads some data out of it and shows that data on standard output; and
  • if the fd wasn't readable but some other event happened (which is probably POLLHUP), it closes the fd.

Now picture this: you run the program in one terminal and ask it to open a FIFO:

$ mkfifo myfifo;
$ ./poll_input myfifo;

Then in a second terminal window, you open that FIFO for writing, write some data into it, and close it:

$ echo aaaaabbbbbccccc > myfifo;

And back in the terminal where the program is running? You'd see this:

Opened "myfifo" on fd 3
About to poll()
Ready: 1
  fd=3; events: POLLIN POLLHUP
    read 10 bytes: aaaaabbbbb
About to poll()
Ready: 1
  fd=3; events: POLLIN POLLHUP
    read 6 bytes: ccccc

About to poll()
Ready: 1
  fd=3; events: POLLHUP
    closing fd 3
All file descriptors closed.  Bye.

So in that output, poll() returned three times, and here's the tea on each one: ๐Ÿซ–

  • First return: the bits in the revents field were POLLIN (meaning the fd is readable) and POLLHUP (meaning the other end of the FIFO got closed). The program then ate some of the available input.
  • Second return from poll() also said POLLIN and POLLHUP โ€” and the program consumed the last of the available input.
  • On the final return, poll() reported only POLLHUP on the FIFO, so the fd got closed and the program said bye bye. ๐Ÿ‘‹

๐Ÿงš Program source

/* poll_input.c

   Licensed under GNU General Public License v2 or later.
*/
#include <err.h>
#include <fcntl.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

int
main(int argc, char *argv[])
{
    int            ready;
    char           buf[10];
    nfds_t         num_open_fds, nfds;
    ssize_t        s;
    struct pollfd  *pfds;

    if (argc < 2) {
       fprintf(stderr, "Usage: %s file...\n", argv[0]);
       exit(EXIT_FAILURE);
    }

    num_open_fds = nfds = argc - 1;
    pfds = calloc(nfds, sizeof(struct pollfd));
    if (pfds == NULL)
        err(EXIT_FAILURE, "malloc");

    /* Open each file on command line, and add it to 'pfds' array.  */

    for (nfds_t j = 0; j < nfds; j++) {
        pfds[j].fd = open(argv[j + 1], O_RDONLY);
        if (pfds[j].fd == -1)
            err(EXIT_FAILURE, "open");

        printf("Opened \"%s\" on fd %d\n", argv[j + 1], pfds[j].fd);

        pfds[j].events = POLLIN;
    }

    /* Keep calling poll() as long as at least one file descriptor is
       open.  */

    while (num_open_fds > 0) {
        printf("About to poll()\n");
        ready = poll(pfds, nfds, -1);
        if (ready == -1)
            err(EXIT_FAILURE, "poll");

        printf("Ready: %d\n", ready);

        /* Deal with array returned by poll().  */

        for (nfds_t j = 0; j < nfds; j++) {
            if (pfds[j].revents != 0) {
                printf("  fd=%d; events: %s%s%s\n", pfds[j].fd,
                       (pfds[j].revents & POLLIN)  ? "POLLIN "  : "",
                       (pfds[j].revents & POLLHUP) ? "POLLHUP " : "",
                       (pfds[j].revents & POLLERR) ? "POLLERR " : "");

                if (pfds[j].revents & POLLIN) {
                    s = read(pfds[j].fd, buf, sizeof(buf));
                    if (s == -1)
                        err(EXIT_FAILURE, "read");
                    printf("    read %zd bytes: %.*s\n",
                           s, (int) s, buf);
                } else {                /* POLLERR | POLLHUP */
                    printf("    closing fd %d\n", pfds[j].fd);
                    if (close(pfds[j].fd) == -1)
                        err(EXIT_FAILURE, "close");
                    num_open_fds--;
                }
            }
        }
    }

    printf("All file descriptors closed.  Bye.\n");
    exit(EXIT_SUCCESS);
}

๐Ÿ’œ SEE ALSO ๐Ÿ’œ


And that's the whole page, bestie!! ๐Ÿฆ‹ You made it to the very end, which is giving academic weapon energy fr fr. โœจ

Quick li'l sign-off so you know exactly which version of this page you just devoured: it's from Linux man-pages 6.20, dated 2026-02-08, and it's all about poll(2). Screenshot it, bookmark it, tattoo it on your forearm โ€” whatever works for you. ๐Ÿ’– Bye!! ๐Ÿ‘‘

๐Ÿ“ฆ 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