简介
当您编写Linux驱动程序,模块或内核程序时,某些进程应等待或休眠某些事件。在Linux中,有几种处理睡眠和唤醒的方法,每种方法都适合不同的需求。Waitqueue也是处理这种情况的方法之一。
每当进程必须等待事件(例如数据到达或进程终止)时,它就应该进入睡眠状态。睡眠会导致进程挂起执行,从而释放处理器以供其他用途。一段时间后,该过程将被唤醒,并在我们等待的事件到达时继续其工作。
等待队列是内核提供的用于实现等待的机制。顾名思义,waitqueue是等待事件的进程列表。换句话说,当特定条件为真时,使用等待队列来等待有人将您唤醒。必须小心使用它们,以确保没有比赛状况。
使用步骤
动态
wait_queue_head_t wq; init_waitqueue_head (&wq);
<a name="e7tIz"></a>### 睡眠API```cwait_event(wq_head, condition)wait_event_timeout(wq, condition, timeout);wait_event_cmd(wq, condition, cmd1, cmd2);wait_event_interruptible(wq, condition);wait_event_interruptible_timeout(wq, condition, timeout);wait_event_killable(wq, condition);
唤醒API
wake_up(&wq)wake_up_all(&wq);wake_up_interruptible(&wq);wake_up_sync(&wq);wake_up_interruptible_sync(&wq);
静态方法创建
#include <linux/kernel.h>#include <linux/init.h>#include <linux/module.h>#include <linux/kdev_t.h>#include <linux/fs.h>#include <linux/cdev.h>#include <linux/device.h>#include <linux/slab.h> //kmalloc()#include <linux/uaccess.h> //copy_to/from_user()#include <linux/kthread.h>#include <linux/wait.h> // Required for the wait queuesuint32_t read_count = 0;static struct task_struct *wait_thread;DECLARE_WAIT_QUEUE_HEAD(wait_queue_etx);dev_t dev = 0;static struct class *dev_class;static struct cdev etx_cdev;int wait_queue_flag = 0;static int __init etx_driver_init(void);static void __exit etx_driver_exit(void);/*************** Driver Fuctions **********************/static int etx_open(struct inode *inode, struct file *file);static int etx_release(struct inode *inode, struct file *file);static ssize_t etx_read(struct file *filp, char __user *buf, size_t len, loff_t *off);static ssize_t etx_write(struct file *filp, const char *buf, size_t len, loff_t *off);static struct file_operations fops ={.owner = THIS_MODULE,.read = etx_read,.write = etx_write,.open = etx_open,.release = etx_release,};static int wait_function(void *unused){while (1){printk(KERN_INFO "Waiting For Event...\n");wait_event_interruptible(wait_queue_etx, wait_queue_flag != 0);if (wait_queue_flag == 2){printk(KERN_INFO "Event Came From Exit Function\n");return 0;}printk(KERN_INFO "Event Came From Read Function - %d\n", ++read_count);wait_queue_flag = 0;}do_exit(0);return 0;}static int etx_open(struct inode *inode, struct file *file){printk(KERN_INFO "Device File Opened...!!!\n");return 0;}static int etx_release(struct inode *inode, struct file *file){printk(KERN_INFO "Device File Closed...!!!\n");return 0;}static ssize_t etx_read(struct file *filp, char __user *buf, size_t len, loff_t *off){printk(KERN_INFO "Read Function\n");wait_queue_flag = 1;wake_up_interruptible(&wait_queue_etx);return 0;}static ssize_t etx_write(struct file *filp, const char __user *buf, size_t len, loff_t *off){printk(KERN_INFO "Write function\n");return 0;}static int __init etx_driver_init(void){/*Allocating Major number*/if ((alloc_chrdev_region(&dev, 0, 1, "etx_Dev")) < 0){printk(KERN_INFO "Cannot allocate major number\n");return -1;}printk(KERN_INFO "Major = %d Minor = %d \n", MAJOR(dev), MINOR(dev));/*Creating cdev structure*/cdev_init(&etx_cdev, &fops);etx_cdev.owner = THIS_MODULE;etx_cdev.ops = &fops;/*Adding character device to the system*/if ((cdev_add(&etx_cdev, dev, 1)) < 0){printk(KERN_INFO "Cannot add the device to the system\n");goto r_class;}/*Creating struct class*/if ((dev_class = class_create(THIS_MODULE, "etx_class")) == NULL){printk(KERN_INFO "Cannot create the struct class\n");goto r_class;}/*Creating device*/if ((device_create(dev_class, NULL, dev, NULL, "etx_device")) == NULL){printk(KERN_INFO "Cannot create the Device 1\n");goto r_device;}//Initialize wait queueinit_waitqueue_head(&wait_queue_etx);//Create the kernel thread with name 'mythread'wait_thread = kthread_create(wait_function, NULL, "WaitThread");if (wait_thread){printk("Thread Created successfully\n");wake_up_process(wait_thread);}elseprintk(KERN_INFO "Thread creation failed\n");printk(KERN_INFO "Device Driver Insert...Done!!!\n");return 0;r_device:class_destroy(dev_class);r_class:unregister_chrdev_region(dev, 1);return -1;}void __exit etx_driver_exit(void){wait_queue_flag = 2;wake_up_interruptible(&wait_queue_etx);device_destroy(dev_class, dev);class_destroy(dev_class);cdev_del(&etx_cdev);unregister_chrdev_region(dev, 1);printk(KERN_INFO "Device Driver Remove...Done!!!\n");}module_init(etx_driver_init);module_exit(etx_driver_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("zhouchengzhu <1073355312@qq.com>");MODULE_DESCRIPTION("A Sample Static Waitqueue Demo");MODULE_VERSION("2:1.0");
动态创建
#include <linux/kernel.h>#include <linux/init.h>#include <linux/module.h>#include <linux/kdev_t.h>#include <linux/fs.h>#include <linux/cdev.h>#include <linux/device.h>#include <linux/slab.h> //kmalloc()#include <linux/uaccess.h> //copy_to/from_user()#include <linux/kthread.h>#include <linux/wait.h> // Required for the wait queuesuint32_t read_count = 0;static struct task_struct *wait_thread;dev_t dev = 0;static struct class *dev_class;static struct cdev etx_cdev;wait_queue_head_t wait_queue_etx;int wait_queue_flag = 0;static int __init etx_driver_init(void);static void __exit etx_driver_exit(void);/*************** Driver Fuctions **********************/static int etx_open(struct inode *inode, struct file *file);static int etx_release(struct inode *inode, struct file *file);static ssize_t etx_read(struct file *filp, char __user *buf, size_t len, loff_t *off);static ssize_t etx_write(struct file *filp, const char *buf, size_t len, loff_t *off);static struct file_operations fops ={.owner = THIS_MODULE,.read = etx_read,.write = etx_write,.open = etx_open,.release = etx_release,};static int wait_function(void *unused){while (1){printk(KERN_INFO "Waiting For Event...\n");wait_event_interruptible(wait_queue_etx, wait_queue_flag != 0);if (wait_queue_flag == 2){printk(KERN_INFO "Event Came From Exit Function\n");return 0;}printk(KERN_INFO "Event Came From Read Function - %d\n", ++read_count);wait_queue_flag = 0;}return 0;}static int etx_open(struct inode *inode, struct file *file){printk(KERN_INFO "Device File Opened...!!!\n");return 0;}static int etx_release(struct inode *inode, struct file *file){printk(KERN_INFO "Device File Closed...!!!\n");return 0;}static ssize_t etx_read(struct file *filp, char __user *buf, size_t len, loff_t *off){printk(KERN_INFO "Read Function\n");wait_queue_flag = 1;wake_up_interruptible(&wait_queue_etx);return 0;}static ssize_t etx_write(struct file *filp, const char __user *buf, size_t len, loff_t *off){printk(KERN_INFO "Write function\n");return 0;}static int __init etx_driver_init(void){/*Allocating Major number*/if ((alloc_chrdev_region(&dev, 0, 1, "etx_Dev")) < 0){printk(KERN_INFO "Cannot allocate major number\n");return -1;}printk(KERN_INFO "Major = %d Minor = %d \n", MAJOR(dev), MINOR(dev));/*Creating cdev structure*/cdev_init(&etx_cdev, &fops);/*Adding character device to the system*/if ((cdev_add(&etx_cdev, dev, 1)) < 0){printk(KERN_INFO "Cannot add the device to the system\n");goto r_class;}/*Creating struct class*/if ((dev_class = class_create(THIS_MODULE, "etx_class")) == NULL){printk(KERN_INFO "Cannot create the struct class\n");goto r_class;}/*Creating device*/if ((device_create(dev_class, NULL, dev, NULL, "etx_device")) == NULL){printk(KERN_INFO "Cannot create the Device 1\n");goto r_device;}//Initialize wait queueinit_waitqueue_head(&wait_queue_etx);//Create the kernel thread with name 'mythread'wait_thread = kthread_create(wait_function, NULL, "WaitThread");if (wait_thread){printk("Thread Created successfully\n");wake_up_process(wait_thread);}elseprintk(KERN_INFO "Thread creation failed\n");printk(KERN_INFO "Device Driver Insert...Done!!!\n");return 0;r_device:class_destroy(dev_class);r_class:unregister_chrdev_region(dev, 1);return -1;}void __exit etx_driver_exit(void){wait_queue_flag = 2;wake_up_interruptible(&wait_queue_etx);device_destroy(dev_class, dev);class_destroy(dev_class);cdev_del(&etx_cdev);unregister_chrdev_region(dev, 1);printk(KERN_INFO "Device Driver Remove...Done!!!\n");}module_init(etx_driver_init);module_exit(etx_driver_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("zhouchengzhu <1073355312@qq.com>");MODULE_DESCRIPTION("A Sample Dynamic Waitqueue Demo");MODULE_VERSION("2:1.0");
