In this lesson, you will learn how to monitor multiple hardware status changes simultaneously without consuming excessive CPU cycles using select and poll.
The Problem with Busy-Waiting
When interacting with hardware peripherals via device nodes (e.g., GPIO inputs, sensors, or serial ports), a naive approach is to use a while(1) loop to repeatedly call read() on a file descriptor. This "busy-waiting" is disastrous for embedded systems: it keeps the CPU at 100% utilization, wastes power, and prevents the system from handling other tasks.
The Multiplexing Solution
Instead of checking each peripheral individually, we use I/O multiplexing. The poll() system call allows your application to "sleep" while the kernel monitors a set of file descriptors on your behalf. When any of the monitored hardware events occur—such as a data packet arriving on a UART or a GPIO pin state change—the kernel wakes up your process.
Implementation Pattern
Using poll involves three main steps: initializing a pollfd structure, calling poll(), and checking the returned events.
struct pollfd fds[2];
// Monitor two different hardware inputs
fds[0].fd = open("/dev/gpio_button", O_RDONLY);
fds[0].events = POLLIN; // Monitor for "data available"
fds[1].fd = open("/dev/sensor_data", O_RDONLY);
fds[1].events = POLLIN;
while (1) {
// Blocks here, consuming zero CPU cycles until an event occurs
int ret = poll(fds, 2, -1);
if (fds[0].revents & POLLIN) {
// Handle button press
}
if (fds[1].revents & POLLIN) {
// Handle sensor data
}
}
By passing -1 as the timeout, the application remains in a suspended state, leaving the CPU free for other processes or power-saving modes. The kernel essentially creates an interrupt-driven bridge between your user-space application and the hardware driver. When the driver detects a physical change, it triggers a "wait queue," which transitions your process from a WAITING state back to RUNNING almost instantaneously.
Key Takeaway: Use poll or select to offload event monitoring to the kernel, replacing CPU-intensive busy-wait loops with efficient, interrupt-driven sleep states.