Non-Blocking IO and Multiplexing Mechanisms

Non-Blocking I/O

The recv function can operate in a non-blocking manner by passing the MSG_DONTWAIT flag as its final argument, though this approach is often cumbersome for consistent behavior. A more persistent method involves specifying the O_NONBLOCK flag during the open system call.

Alternatively, the fcntl function modifies the status flags of an existing file descriptor, instructing the kernel to treat it as non-blocking.

#include <unistd.h>
#include <fcntl.h>

int fcntl(int fd, int cmd, ... /* arg */ );
// Commands include:
// F_DUPFD: Duplicate file descriptor
// F_GETFD / F_SETFD: Get/set file descriptor flags
// F_GETFL / F_SETFL: Get/set file status flags (e.g., O_NONBLOCK)
// F_GETOWN / F_SETOWN: Get/set asynchronous I/O ownership
// F_GETLK / F_SETLK / F_SETLKW: Get/set record locks

To configure a descriptor for non-blocking operation, retrieve its current flags and apply a bitwise OR with O_NONBLOCK.

void enable_nonblocking(int descriptor) {
    int current_flags = fcntl(descriptor, F_GETFL, 0);
    if (current_flags == -1) {
        perror("Failed to get flags");
        return;
    }
    if (fcntl(descriptor, F_SETFL, current_flags | O_NONBLOCK) == -1) {
        perror("Failed to set non-blocking flag");
    }
}

On Linux terminals, pressing Ctrl+D signals the end of standard input (EOF).

When a non-blocking descriptor is read but no data is available, system calls like read or recv return an error status. This does not signify a true failure; rather, it indicates that the resource is temporarily unavailable. The errno variable is set to EAGAIN (or EWOULDBLOCK, typically value 11) to distinguish this condition from actual errors.

#include <iostream>
#include <unistd.h>
#include <cerrno>
#include <cstring>

char buffer[1024];
while (true) {
    ssize_t bytes_read = read(STDIN_FILENO, buffer, sizeof(buffer) - 1);
    if (bytes_read > 0) {
        buffer[bytes_read] = '\0';
        std::cout << "Echo: " << buffer << std::endl;
    } else if (bytes_read == 0) {
        std::cout << "Input closed." << std::endl;
        break;
    } else {
        if (errno == EWOULDBLOCK || errno == EAGAIN) {
            std::cerr << "Data not ready, waiting... (errno: " << errno << ")" << std::endl;
            sleep(1);
        } else {
            perror("Read failure");
            break;
        }
    }
}

I/O Multiplexing

Polling non-blocking descriptors can consume unnecessary CPU cycles. I/O multiplexing provides an efficient alternative to monitor multiple file descriptors simultaneously. Common implementations include select, poll, and epoll.

Tags: non-blocking IO fcntl EAGAIN I/O Multiplexing Linux

Posted on Sat, 09 May 2026 15:05:52 +0000 by supernova