Wait Queues and Blocking I/O
When writing device drivers, you often need a way to let user-space processes wait for events, such as data becoming available. In the kernel, wait queues provide a mechanism for blocking a process until a condition is met. When a process performs a blocking read and the data is not ready, it can be put to sleep on a wait queue, freeing up the CPU for other work until the desired event occurs.
blocking_read_example.c
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576#include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/uaccess.h> #include <linux/wait.h> #include <linux/mutex.h> #define DEVICE_NAME "waitqdemo" #define BUF_SIZE 128 static int major; static char device_buffer[BUF_SIZE]; static int data_available = 0; static DECLARE_WAIT_QUEUE_HEAD(waitq); static DEFINE_MUTEX(buffer_mutex); ssize_t waitq_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { int ret; // Block until data_available is set if (wait_event_interruptible(waitq, data_available)) return -ERESTARTSYS; mutex_lock(&buffer_mutex); ret = simple_read_from_buffer(buf, count, ppos, device_buffer, BUF_SIZE); data_available = 0; // Reset after reading mutex_unlock(&buffer_mutex); return ret; } ssize_t waitq_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { ssize_t ret; mutex_lock(&buffer_mutex); ret = simple_write_to_buffer(device_buffer, BUF_SIZE, ppos, buf, count); data_available = 1; mutex_unlock(&buffer_mutex); // Will notify reader in the write example below // wake_up_interruptible(&waitq); return ret; } struct file_operations waitq_fops = { .owner = THIS_MODULE, .read = waitq_read, .write = waitq_write, }; static int __init waitq_init(void) { major = register_chrdev(0, DEVICE_NAME, &waitq_fops); if (major < 0) return major; pr_info("waitqdemo: loaded with major %d\n", major); return 0; } static void __exit waitq_exit(void) { unregister_chrdev(major, DEVICE_NAME); pr_info("waitqdemo: unloaded\n"); } module_init(waitq_init); module_exit(waitq_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Kernel Course"); MODULE_DESCRIPTION("Wait queue blocking read example");
The code above demonstrates a blocking read operation using a wait queue. The wait_event_interruptible macro puts the calling process to sleep until the data_available flag is set. Once another part of the driver sets this flag and calls wake_up_interruptible, the waiting process is woken up. This is how event notification is implemented in the kernel: after the write operation receives new data, it signals waiting readers by waking them up.
wakeup_write_example.c
12345678910111213141516171819202122232425262728// wakeup_write_example.c #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/uaccess.h> #include <linux/wait.h> #include <linux/mutex.h> extern wait_queue_head_t waitq; extern int data_available; extern char device_buffer[]; #define BUF_SIZE 128 ssize_t waitq_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { ssize_t ret; mutex_lock(&buffer_mutex); ret = simple_write_to_buffer(device_buffer, BUF_SIZE, ppos, buf, count); data_available = 1; mutex_unlock(&buffer_mutex); // Notify any process waiting on the wait queue wake_up_interruptible(&waitq); return ret; }
Thanks for your feedback!
Ask AI
Ask AI
Ask anything or try one of the suggested questions to begin our chat
Can you explain how wait queues differ from other synchronization mechanisms in the kernel?
What happens if the process is interrupted while waiting on the wait queue?
Can you provide an example of how to implement a wait queue in a device driver?
Awesome!
Completion rate improved to 3.85
Wait Queues and Blocking I/O
Swipe to show menu
When writing device drivers, you often need a way to let user-space processes wait for events, such as data becoming available. In the kernel, wait queues provide a mechanism for blocking a process until a condition is met. When a process performs a blocking read and the data is not ready, it can be put to sleep on a wait queue, freeing up the CPU for other work until the desired event occurs.
blocking_read_example.c
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576#include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/uaccess.h> #include <linux/wait.h> #include <linux/mutex.h> #define DEVICE_NAME "waitqdemo" #define BUF_SIZE 128 static int major; static char device_buffer[BUF_SIZE]; static int data_available = 0; static DECLARE_WAIT_QUEUE_HEAD(waitq); static DEFINE_MUTEX(buffer_mutex); ssize_t waitq_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { int ret; // Block until data_available is set if (wait_event_interruptible(waitq, data_available)) return -ERESTARTSYS; mutex_lock(&buffer_mutex); ret = simple_read_from_buffer(buf, count, ppos, device_buffer, BUF_SIZE); data_available = 0; // Reset after reading mutex_unlock(&buffer_mutex); return ret; } ssize_t waitq_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { ssize_t ret; mutex_lock(&buffer_mutex); ret = simple_write_to_buffer(device_buffer, BUF_SIZE, ppos, buf, count); data_available = 1; mutex_unlock(&buffer_mutex); // Will notify reader in the write example below // wake_up_interruptible(&waitq); return ret; } struct file_operations waitq_fops = { .owner = THIS_MODULE, .read = waitq_read, .write = waitq_write, }; static int __init waitq_init(void) { major = register_chrdev(0, DEVICE_NAME, &waitq_fops); if (major < 0) return major; pr_info("waitqdemo: loaded with major %d\n", major); return 0; } static void __exit waitq_exit(void) { unregister_chrdev(major, DEVICE_NAME); pr_info("waitqdemo: unloaded\n"); } module_init(waitq_init); module_exit(waitq_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Kernel Course"); MODULE_DESCRIPTION("Wait queue blocking read example");
The code above demonstrates a blocking read operation using a wait queue. The wait_event_interruptible macro puts the calling process to sleep until the data_available flag is set. Once another part of the driver sets this flag and calls wake_up_interruptible, the waiting process is woken up. This is how event notification is implemented in the kernel: after the write operation receives new data, it signals waiting readers by waking them up.
wakeup_write_example.c
12345678910111213141516171819202122232425262728// wakeup_write_example.c #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/uaccess.h> #include <linux/wait.h> #include <linux/mutex.h> extern wait_queue_head_t waitq; extern int data_available; extern char device_buffer[]; #define BUF_SIZE 128 ssize_t waitq_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { ssize_t ret; mutex_lock(&buffer_mutex); ret = simple_write_to_buffer(device_buffer, BUF_SIZE, ppos, buf, count); data_available = 1; mutex_unlock(&buffer_mutex); // Notify any process waiting on the wait queue wake_up_interruptible(&waitq); return ret; }
Thanks for your feedback!