Waitqueue in Linux

本文是Linux设备驱动程序系列的续篇,并对字符驱动程序及其实现进行了讨论。这是Linux设备驱动程序教程的第10部分。现在,我们将讨论Linux中的Waitqueue。

Linux中的等待队列

介绍

当您编写Linux驱动程序,模块或内核程序时,某些进程应为某些事件等待或休眠。在Linux中有几种处理睡眠和唤醒的方法,每种方法都适合不同的需求。 Waitqueue也是处理这种情况的方法之一。

每当进程必须等待事件(例如数据到达或进程终止)时,它就应该进入睡眠状态。睡眠会导致进程挂起执行,将处理器释放出来以用于其他用途。一段时间后,该过程将被唤醒,并在我们等待的事件到达时继续其工作。
Waitqueue中有3个重要步骤。

  1. 初始化等待队列
  2. 排队(使任务进入睡眠状态,直到事件发生)
  3. 唤醒排队的任务

初始化等待队列

将此头文件用于Waitqueue(#include /linux/wait.h)。有两种初始化等待队列的方法。

  1. 静态方法
  2. 动态方法
    您可以使用任何一种方法。

静态方法

DECLARE_WAIT_QUEUE_HEAD(wq);

其中“ wq”是将使任务进入休眠状态的队列的名称。

动态方法

wait_queue_head_t wq;
init_waitqueue_head(&wq);

您可以使用上述任何一种方法来创建等待队列。

排队

一旦声明并初始化了等待队列,进程便可以使用它进入睡眠状态。有几个宏可用于不同用途。我们将一一看到。

wait_event
wait_event_timeout
wait_event_cmd
wait_event_interruptible
wait_event_interruptible_timeout
wait_event_killable

旧的内核版本使用函数sleep_on()interruptible_sleep_on(),但是这两个函数会引入不良的竞争条件,因此不应使用。

每当我们使用上面的宏之一时,它将把该任务添加到由我们创建的等待队列中。然后它将等待事件。

wait_event

休眠直到条件成真。
wait_event(wq, condition);
wq:等待的等待队列
condition:等待事件的C表达式
进程进入睡眠状态(TASK_UNINTERRUPTIBLE),直到条件评估为true。每次唤醒等待队列wq时都会检查条件。

wait_event_timeout

休眠直到条件成真或者超时。
wait_event_timeout(wq, condition, timeout);
wq:等待的等待队列
condition:等待事件的C表达式
timeout – 超时, 单位jiffies
进程进入睡眠状态(TASK_UNINTERRUPTIBLE),直到条件评估为true或者超时。每次唤醒等待队列wq时都会检查条件。
如果在超时后将条件评估为假,则返回0;如果在超时后将条件评估为真,则返回1;如果在超时之前将条件评估为真,则返回剩余的jiffies(至少1)。

wait_event_cmd

休眠直到条件成真。
wait_event_cmd(wq, condition, cmd1, cmd2);
wq:等待的等待队列
condition:等待事件的C表达式
cmd1:该命令将在睡眠前执行
cmd2:该命令将在睡眠后执行
进程进入睡眠状态(TASK_UNINTERRUPTIBLE),直到条件评估为true。每次唤醒等待队列wq时都会检查条件。

wait_event_interruptible

休眠直到条件成真。
wait_event_interruptible(wq, condition);
wq:等待的等待队列
condition:等待事件的C表达式
cmd1:该命令将在睡眠前执行
cmd2:该命令将在睡眠后执行
进程进入睡眠状态(TASK_UNINTERRUPTIBLE),直到条件评估为true。每次唤醒等待队列wq时都会检查条件。
如果该函数被信号中断,则该函数将返回-ERESTARTSYS;如果条件被评估为true,则该函数将返回0。

wait_event_interruptible_timeout

休眠直到条件成真或者超时。
wait_event_timeout(wq, condition, timeout);
wq:等待的等待队列
condition:等待事件的C表达式
timeout – 超时, 单位jiffies
进程进入睡眠状态(TASK_INTERRUPTIBLE),直到条件评估为true或收到信号或超时超时。每次唤醒等待队列wq时都会检查条件。
返回:
如果在超时后将条件评估为false,则返回0;
如果在超时后将条件评估为true,则返回1;
如果在超时之前将条件评估为true,则返回剩余的抖动(至少1),或者返回-ERESTARTSYS它被信号打断了。

wait_event_killable

休眠直到条件成真
wait_event_killable(wq, condition);
wq:等待的等待队列
condition:等待事件的C表达式
该过程进入睡眠状态(TASK_KILLABLE),直到条件评估为true或收到信号为止。每次唤醒等待队列wq时都会检查条件。
如果该函数被信号中断,则该函数将返回-ERESTARTSYS;如果条件被评估为true,则该函数将返回0

唤醒等待的任务

当某些任务由于等待队列而进入睡眠模式时,我们可以使用以下功能唤醒这些任务。

wake_up
wake_up_all
wake_up_interruptible
wake_up_sync and wake_up_interruptible_sync

wake_up

从处于不间断睡眠的等待队列中仅唤醒一个进程。
wake_up(&wq);
wq:唤醒的等待队列

wake_up_all

唤醒等待队列中的所有进程
wake_up_all(&wq);
wq:唤醒的等待队列

wake_up_interruptible

从处于中断睡眠状态的等待队列中仅唤醒一个进程
wake_up_interruptible(&wq);
wq:唤醒的等待队列

wake_up_sync and wake_up_interruptible_sync

wake_up_sync(&wq);
wake_up_interruptible_sync(&wq);

通常,wake_up调用会导致立即重新安排,这意味着其他进程可能在wake_up返回之前运行。相反,“同步”变体使所有唤醒的进程都可运行,但不会重新调度CPU。这用于避免在已知当前进程即将进入睡眠状态时进行重新计划,从而仍然强制重新计划。请注意,唤醒的进程可以立即在其他处理器上运行,因此不应期望这些功能提供相互排斥。

WaitQueue in Linux

首先,我将向您解释驱动程序代码的概念。
在此源代码中,我们在两个地方发送了一个wake_up。一个来自读取功能,另一个来自驱动程序退出功能。
我创建了一个具有while(1)的线程(wait_function)。该线程将始终等待事件。它将一直休眠,直到得到一个wake_up调用。当它获得了wake_up调用时,它将检查条件。如果条件为1,则唤醒来自读取功能。如果为2,则唤醒来自退出功能。如果wake_up来自读取函数,它将打印读取计数并再次等待。如果来自退出函数,它将从线程退出。
在这里,我添加了两个版本的代码。

  1. 静态方法创建的等待队列
  2. 通过动态方法创建的等待队列

通过静态方法创建的等待队列

github

/***************************************************************************//**
*  \file       driver.c
*
*  \details    Simple linux driver (Waitqueue Static method)
*
*  \author     EmbeTronicX
*
* *******************************************************************************/
#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 queues
 
 
uint32_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;
 
/*
** Function Prototypes
*/
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);
 
/*
** File operation sturcture
*/
static struct file_operations fops =
{
        .owner          = THIS_MODULE,
        .read           = etx_read,
        .write          = etx_write,
        .open           = etx_open,
        .release        = etx_release,
};
 
/*
** Thread function
*/
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;
}
 
/*
** This fuction will be called when we open the Device file
*/
static int etx_open(struct inode *inode, struct file *file)
{
        printk(KERN_INFO "Device File Opened...!!!\n");
        return 0;
}
 
/*
** This fuction will be called when we close the Device file
*/
static int etx_release(struct inode *inode, struct file *file)
{
        printk(KERN_INFO "Device File Closed...!!!\n");
        return 0;
}
 
/*
** This fuction will be called when we read the Device file
*/
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;
}
 
/*
** This fuction will be called when we write the Device file
*/
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;
}
 
/*
** Module Init function
*/
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 queue
        init_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);
        } else
                printk(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;
}
 
/*
** Module exit function
*/ 
static 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("EmbeTronicX <embetronicx@gmail.com>");
MODULE_DESCRIPTION("Simple linux driver (Waitqueue Static method)");
MODULE_VERSION("1.7");

通过动态方法创建等待队列

GitHub

/****************************************************************************//**
*  \file       driver.c
*
*  \details    Simple linux driver (Waitqueue Dynamic method)
*
*  \author     EmbeTronicX
*
* *******************************************************************************/
#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 queues
 
 
uint32_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;
 
/*
** Function Prototypes
*/
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);
 
/*
** File operation sturcture
*/
static struct file_operations fops =
{
        .owner          = THIS_MODULE,
        .read           = etx_read,
        .write          = etx_write,
        .open           = etx_open,
        .release        = etx_release,
};
 
/*
** Thread function
*/
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;
}
 
/*
** This fuction will be called when we open the Device file
*/ 
static int etx_open(struct inode *inode, struct file *file)
{
        printk(KERN_INFO "Device File Opened...!!!\n");
        return 0;
}
 
/*
** This fuction will be called when we close the Device file
*/
static int etx_release(struct inode *inode, struct file *file)
{
        printk(KERN_INFO "Device File Closed...!!!\n");
        return 0;
}
 
/*
** This fuction will be called when we read the Device file
*/
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;
}
 
/*
** This fuction will be called when we write the Device file
*/
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;
}
 
/*
** Module Init function
*/
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 queue
        init_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);
        } else
                printk(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;
}
 
/*
** Module exit function
*/
static 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("EmbeTronicX <embetronicx@gmail.com>");
MODULE_DESCRIPTION("Simple linux driver (Waitqueue Dynamic method)");
MODULE_VERSION("1.8");

MakeFile

obj-m += driver.o
KDIR = /lib/modules/$(shell uname -r)/build
all:
    make -C $(KDIR)  M=$(shell pwd) modules
clean:
    make -C $(KDIR)  M=$(shell pwd) clean

编译测试

make
insmod driver.ko
dmesg

因此该线程正在等待事件。现在我们将通过使用

sudo cat /dev/etx_device

读取驱动程序来发送事件
我们发送来自读取功能的唤醒,因此它将打印读取计数,然后再次进入睡眠状态。现在通过sudo rmmod驱动程序从退出函数发送事件
现在条件是2。​​因此它将从线程返回并删除驱动程序。
这就是等待队列。在下一个教程中,我们将讨论Linux设备驱动程序中的sysfs

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值