Topics Covered In This Free Linux Kernel Development Course Lecture
wait queue linux kernel
EPOLLIN EPOLLOUT kernel 6.x
free linux device drivers course
free embedded systems course
select poll epoll character driver
The poll method in a Linux character device driver is what lets a user-space program ask
the kernel “is my device ready yet?” instead of endlessly checking in a loop. Any application that calls
poll(), select(), or epoll_wait() on your device file ends up inside the
.poll callback of your driver’s file_operations structure. In this lecture of our
free Linux kernel development course we extend the ep_chardev driver built earlier in this free
linux device drivers course with a working, modern ep_poll() implementation, using the current
kernel 6.x EPOLLIN/EPOLLOUT event model instead of the old POLLIN/POLLOUT macros.
What You Will Learn
wait_queue_head_t basics
poll_wait() internals
__poll_t return type
EPOLLIN vs old POLLIN
wake_up_interruptible()
Full ep_poll_demo driver
Testing with a real poll() program
Prerequisites
write() method (Lecture 4.7)
read() method (Lecture 4.8)
llseek() method (Lecture 4.9)
Basic C and a kernel 6.x build environment
What Is the poll() Method in a Linux Character Device Driver?
The poll entry inside struct file_operations is the hook the kernel calls whenever a
user-space process runs poll(), select(), or epoll_wait() on a file
descriptor that belongs to your device. Its job is simple: tell the kernel right now whether the device has data
to read, room to write, or neither — and if neither, register the calling process on a wait queue so it can be
woken up later without burning CPU cycles in a busy loop.
Why User-Space Needs poll(), select(), and epoll()
Without a poll method, a program that wants to know “is data ready?” has only two bad options: call
read() and block until something arrives, or call a non-blocking read() repeatedly in a
loop — wasting CPU. The poll() system call (and its faster cousin epoll) lets one thread
watch many file descriptors at once and sleep until at least one of them is ready. Network servers, GUI event
loops, and daemons that talk to multiple devices all rely on this.
| Approach | Behaviour | CPU Cost |
|---|---|---|
| Blocking read() | Sleeps until data arrives on one fd | Low, but only one fd at a time |
| Non-blocking read() in a loop | Keeps checking manually | High, wastes cycles |
| poll()/select()/epoll() | Sleeps until any watched fd is ready | Low, scales to many fds |
Kernel Internals: wait_queue_head_t and poll_wait()
Every event your driver can wait on needs a wait_queue_head_t. It is created with
DECLARE_WAIT_QUEUE_HEAD() at compile time or init_waitqueue_head() at runtime. Inside
your .poll callback, you call poll_wait(file, &queue, wait) for every wait queue you
care about — this does not sleep by itself, it just registers the calling process with that queue so a later
wake_up_interruptible() call can find it.
Old POLLIN vs Modern EPOLLIN: What Changed in Kernel 6.x
Older references (and many outdated tutorials) show the .poll callback returning plain
unsigned int with flags like POLLIN and POLLRDNORM. Since Linux 4.16, the
correct return type is __poll_t, and the preferred flags are the EPOLLxxx versions
defined in <uapi/linux/eventpoll.h>. The old POLLIN-style macros still exist for
compatibility, but new drivers should use the modern names directly.
| Old Style (Pre-4.16) | Modern Kernel 6.x Style | Meaning |
|---|---|---|
| unsigned int poll() | __poll_t poll() | Return type of the poll callback |
| POLLIN | POLLRDNORM | EPOLLIN | EPOLLRDNORM | Data available to read |
| POLLOUT | POLLWRNORM | EPOLLOUT | EPOLLWRNORM | Buffer space available to write |
Note: functionally the bit values are the same today, but using the EPOLLxxx
names and __poll_t type keeps your driver aligned with current upstream style and avoids sparse/
checkpatch warnings.
Writing the ep_poll() Driver Method
Below is an original, from-scratch ep_poll_demo driver written for this free linux device
drivers course. It keeps a small fixed-size ring buffer, and implements open, read,
write, and poll so you can see how the poll callback works together with the other
file operations.
// ep_poll_demo.c — original example driver for this lecture
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/poll.h>
#include <linux/wait.h>
#include <linux/mutex.h>
#define EP_BUF_SIZE 128
#define EP_DEVICE_NAME "ep_poll_demo"
static dev_t ep_devno;
static struct cdev ep_cdev;
static struct mutex ep_lock;
static char ep_buf[EP_BUF_SIZE];
static size_t ep_data_len; /* how many bytes currently held */
static DECLARE_WAIT_QUEUE_HEAD(ep_read_wq);
static DECLARE_WAIT_QUEUE_HEAD(ep_write_wq);
static int ep_open(struct inode *inode, struct file *filp)
{
return 0;
}
static ssize_t ep_read(struct file *filp, char __user *ubuf,
size_t count, loff_t *fpos)
{
ssize_t ret;
mutex_lock(&ep_lock);
if (ep_data_len == 0) {
mutex_unlock(&ep_lock);
if (filp->f_flags & O_NONBLOCK)
return -EAGAIN;
if (wait_event_interruptible(ep_read_wq, ep_data_len > 0))
return -ERESTARTSYS;
mutex_lock(&ep_lock);
}
if (count > ep_data_len)
count = ep_data_len;
if (copy_to_user(ubuf, ep_buf, count)) {
mutex_unlock(&ep_lock);
return -EFAULT;
}
ep_data_len -= count;
ret = count;
mutex_unlock(&ep_lock);
wake_up_interruptible(&ep_write_wq);
return ret;
}
static ssize_t ep_write(struct file *filp, const char __user *ubuf,
size_t count, loff_t *fpos)
{
mutex_lock(&ep_lock);
if (count > EP_BUF_SIZE - ep_data_len)
count = EP_BUF_SIZE - ep_data_len;
if (count == 0) {
mutex_unlock(&ep_lock);
return -ENOSPC;
}
if (copy_from_user(ep_buf, ubuf, count)) {
mutex_unlock(&ep_lock);
return -EFAULT;
}
ep_data_len = count;
mutex_unlock(&ep_lock);
wake_up_interruptible(&ep_read_wq);
return count;
}
static __poll_t ep_poll(struct file *filp, poll_table *wait)
{
__poll_t mask = 0;
poll_wait(filp, &ep_read_wq, wait);
poll_wait(filp, &ep_write_wq, wait);
mutex_lock(&ep_lock);
if (ep_data_len > 0)
mask |= EPOLLIN | EPOLLRDNORM;
if (ep_data_len < EP_BUF_SIZE)
mask |= EPOLLOUT | EPOLLWRNORM;
mutex_unlock(&ep_lock);
return mask;
}
static const struct file_operations ep_fops = {
.owner = THIS_MODULE,
.open = ep_open,
.read = ep_read,
.write = ep_write,
.poll = ep_poll,
};
static int __init ep_poll_demo_init(void)
{
int ret;
ret = alloc_chrdev_region(&ep_devno, 0, 1, EP_DEVICE_NAME);
if (ret < 0)
return ret;
mutex_init(&ep_lock);
cdev_init(&ep_cdev, &ep_fops);
ret = cdev_add(&ep_cdev, ep_devno, 1);
if (ret < 0) {
unregister_chrdev_region(ep_devno, 1);
return ret;
}
pr_info("ep_poll_demo: loaded, major=%d minor=%d\n",
MAJOR(ep_devno), MINOR(ep_devno));
return 0;
}
static void __exit ep_poll_demo_exit(void)
{
cdev_del(&ep_cdev);
unregister_chrdev_region(ep_devno, 1);
pr_info("ep_poll_demo: unloaded\n");
}
module_init(ep_poll_demo_init);
module_exit(ep_poll_demo_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("EmbeddedPathashala poll() method demo driver");
Building and Loading the Module
Create a matching Makefile and build against your running kernel headers:
# Makefile
obj-m += ep_poll_demo.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
$ make
$ sudo insmod ep_poll_demo.ko
$ dmesg | tail -n 2
[ 1234.567890] ep_poll_demo: loaded, major=238 minor=0
$ sudo mknod /dev/ep_poll_demo c 238 0
$ sudo chmod 666 /dev/ep_poll_demo
Testing poll() From a User-Space Program
Here is a small, original test program that opens the device and waits on it with poll() before
reading — it prints whether the device became readable, writable, or timed out.
// ep_poll_test.c
#include <stdio.h>
#include <fcntl.h>
#include <poll.h>
#include <unistd.h>
int main(void)
{
struct pollfd pfd;
char buf[64];
int ret;
pfd.fd = open("/dev/ep_poll_demo", O_RDWR);
if (pfd.fd < 0) {
perror("open");
return 1;
}
pfd.events = POLLIN | POLLOUT;
ret = poll(&pfd, 1, 5000); /* wait up to 5 seconds */
if (ret < 0) {
perror("poll");
return 1;
} else if (ret == 0) {
printf("Timed out, device not ready\n");
return 0;
}
if (pfd.revents & POLLIN) {
ret = read(pfd.fd, buf, sizeof(buf) - 1);
buf[ret] = '\0';
printf("Readable! Got: %s\n", buf);
}
if (pfd.revents & POLLOUT)
printf("Device is writable\n");
close(pfd.fd);
return 0;
}
Run it in one terminal, then write to the device from another to see the wake-up happen:
Terminal 1:
$ gcc ep_poll_test.c -o ep_poll_test
$ ./ep_poll_test
Device is writable
Terminal 2 (within 5 seconds):
$ echo "hello from EmbeddedPathashala" > /dev/ep_poll_demo
Terminal 1 output updates to:
Readable! Got: hello from EmbeddedPathashala
Device is writable
Immediately after loading, the buffer is empty and full of free space, so the first run only reports
POLLOUT. Once another process writes to /dev/ep_poll_demo, ep_poll()
returns EPOLLIN as well, poll() in the test program unblocks instantly, and the read
call returns the new data — without the test program ever spinning in a loop.
Common Mistakes When Implementing the poll Method
Sleeping directly inside poll() (never sleep here, only register)
Not calling wake_up_interruptible() after state changes
Using old unsigned int / POLLIN in new drivers
Forgetting O_NONBLOCK handling in read/write
Racing on shared buffer without a mutex or spinlock
Best Practices for poll() and Wait Queues
Always call poll_wait() before checking state
Protect shared state with mutex or spinlock
Prefer EPOLLxxx flags and __poll_t
Keep the poll callback fast, non-blocking
Test with real poll()/select()/epoll clients
Performance and Security Considerations
Performance: the poll callback runs on every call to poll()/select()
from user space, so keep it lightweight — no long loops, no blocking calls, no large memory allocations. Holding
a lock briefly to check a flag is fine; doing real work inside .poll is not.
Security: the poll method itself does not copy user data, but any shared buffer it inspects
must still be protected the same way as in read()/write() — a race window here can
report a device as readable right before another process empties the buffer, so always re-check state after
waking up rather than trusting the poll result blindly.
Summary: Key Takeaways
poll_wait() registers the process, does not sleep
wake_up_interruptible() triggers re-checks
Modern kernels use __poll_t + EPOLLxxx
ep_chardev now supports open/read/write/llseek/poll
Conclusion
With ep_poll() in place, the ep_chardev driver built across this free linux kernel
development course now supports every core file operation a real character driver needs: open, release, read,
write, llseek, and poll. This is exactly the pattern used by real-world drivers — serial ports, input devices,
and custom hardware interfaces — to let user-space applications wait efficiently instead of polling in a busy
loop. In the next lecture of this free linux device drivers course, we move on to ioctl(), the
method used for device-specific commands that don’t fit read/write/seek.
Frequently Asked Questions
What is the poll method used for in a Linux device driver?
It lets the kernel report, on demand, whether a device is ready to be read from or written to, so user-space
programs using poll(), select(), or epoll() can wait efficiently instead of busy-looping.
What is the difference between poll(), select(), and epoll()?
All three let a process wait on multiple file descriptors at once. select() and poll() are older, simpler
interfaces; epoll() scales much better with large numbers of file descriptors and is preferred in modern
Linux applications. All three ultimately call your driver’s same .poll callback.
Why do I need poll_wait() instead of sleeping directly?
The .poll callback must never block. poll_wait() only registers the calling process on a wait queue; the
kernel’s poll/select/epoll infrastructure handles the actual sleeping and wake-up logic.
What changed with EPOLLIN vs the old POLLIN macro?
Since Linux 4.16 the poll callback returns __poll_t instead of unsigned int, and the recommended event
flags are EPOLLIN, EPOLLOUT, EPOLLRDNORM, and EPOLLWRNORM rather than the older POLLIN/POLLOUT names.
Do I need a separate wait queue for read and write?
It is best practice. Using one wait queue for readability and another for writability avoids waking up
processes for events they are not actually interested in.
What happens if I forget to call wake_up_interruptible()?
Processes waiting in poll(), select(), or epoll_wait() on your device will never be woken up when data
becomes available, and they will appear to hang until their timeout expires.
Can the poll method be used with non-blocking I/O?
Yes — poll() is the standard companion to O_NONBLOCK. A program typically opens the device non-blocking,
then uses poll() to know exactly when to call read() or write() so those calls succeed immediately.
Is this free linux kernel development course updated for kernel 6.x?
Yes, every lecture in this free linux device drivers course, including this one, targets current kernel
6.x APIs rather than outdated kernel 2.6-era interfaces found in older books.
Continue This Free Linux Device Drivers Course
Keep building the ep_chardev driver with us — next up is the ioctl() method.
