Skip to content

poll and select

poll() and select() are readiness-based I/O multiplexing APIs. They allow a process to wait until one or more file descriptors become ready for I/O.

Core Concept

wait for readiness
read or write only when the fd is ready

This avoids CPU-wasting busy loops such as repeatedly calling read() until data appears.

Blocking vs Non-blocking I/O

Mode Behavior
Blocking read() Sleeps until data is available
Non-blocking read() Returns -1 with errno = EAGAIN if no data is available
poll() + non-blocking fd Sleeps until an fd is ready, then performs non-blocking I/O

Typical poll Loop

struct pollfd fds[1];

fds[0].fd = fd;
fds[0].events = POLLIN;

while (1) {
    int ret = poll(fds, 1, -1);

    if (ret < 0) {
        if (errno == EINTR)
            continue;
        break;
    }

    if (fds[0].revents & POLLIN) {
        /* Read until the event is consumed. */
        read(fd, buf, sizeof(buf));
    }
}

Relationship with Kernel Drivers

A pollable character driver usually implements:

static __poll_t mydev_poll(struct file *file, poll_table *wait)
{
    poll_wait(file, &ctx->read_wq, wait);

    if (event_available(ctx))
        return EPOLLIN | EPOLLRDNORM;

    return 0;
}

Important points:

  • .poll does not consume data.
  • .poll only reports readiness.
  • Userspace must call read() after the fd becomes ready.
  • If data remains unread, poll() will keep reporting readiness.

Common Pitfalls

Pitfall Result
Forgetting to read after poll() The same fd keeps waking the event loop
Busy loop with non-blocking read() High CPU usage
Treating readiness as data Incorrect design; readiness only means I/O may proceed
Ignoring EINTR Event loop exits unexpectedly when interrupted by signals