Completion in linux

先决条件

在示例部分中,我使用kthread解释了此完成过程。如果您不知道什么是kthread以及如何使用它,那么我建议您使用下面的链接进行探索。

  1. Kthread Tutorial in Linux Kernel
  2. Waitqueue Tutorial in Linux Kernel

Completion

Completion, 和名字本身说的一样。当我们想在完成某些工作时通知或唤醒某个线程或某些东西时,可以使用完成。我们将采取一种情况。我们要等待一个线程运行。在此之前,该线程必须休眠。该过程完成后,我们需要唤醒正在休眠的线程。我们可以通过使用没有竞争条件的完成来做到这一点。

这些完成是一种同步机制,在上述情况下是一种很好的方法,而不是使用不正确的锁/信号量和繁忙循环。

Completion in Linux Device Driver

在Linux内核中,完成是通过使用waitqueue开发的。

使用Completion的优点是它们具有定义明确,目标明确的目的,可以很容易地看到代码的意图,但是由于所有线程都可以继续执行直到实际需要结果,因此它们还可以提高代码效率。使用低级调度程序的睡眠/唤醒功能,等待和信令均非常高效。

Completion中有5个重要步骤。

  1. 初始化completion
  2. 重新初始化completion
  3. 等待completion(代码正在等待并等待完成某件事)
  4. 唤醒任务(将信号发送到睡眠部分)
  5. 检查状态

初始化completion

我们必须#include <linux/completion.h>并创建一个struct completion类型的变量,该变量只有两个字段:

struct completion { 
    unsigned int done; 
    wait_queue_head_t wait; 
};

在这里,wait是等待任务放置的等待队列(如果有的话)。done是用于指示其是否完成的完成标志。
我们可以通过两种方式创建类型变量。

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

静态方法

DECLARE_COMPLETION(data_read_done);

其中data_read_done是将要静态创建的结构的名称。

动态方法

init_completion (struct completion * x);

其中x指向要初始化的completion结构

举例:

struct completion data_read_done;
init_completion(&data_read_done);

在此init_completion调用中,我们初始化等待队列并将完成设置为0,即not completednot done

重新初始化完成

reinit_completion (struct completion * x);

这里x指向要重新初始化的completion结构
举例:

reinit_completion(&data_read_done);

此函数应用于重新初始化completion结构,以便可以重用。在使用complete_all之后,这一点尤其重要。只需将->done字段重置为0(“not done”),而无需触摸等待队列。该函数的调用者必须确保没有并行的wait_for_completion()调用。

等待完成

为了让线程等待一些并发活动完成,它会根据用例调用该函数中的任何一个。

wait_for_completion

这用于使函数等待任务完成。

void wait_for_completion (struct completion * x);

这里x指向保持此特定完成的状态
等待等待完成特定任务的信号。它不是可中断的,没有超时。

例如:

wait_for_completion (&data_read_done);

请注意,wait_for_completion()正在调用spin_lock_irq()/spin_unlock_irq(),因此只有在知道启用了中断的情况下才能安全地调用它。从IRQs-off原子上下文中调用它会导致难以检测到的虚假中断启用。

wait_for_completion_timeout

这用于使函数等待超时的任务完成。超时最好使用msecs_to_jiffies()usecs_to_jiffies()计算,以使代码在很大程度上保持HZ不变。

unsigned long wait_for_completion_timeout (struct completion * x, unsigned long timeout);

x指向保持此特定完成的状态
timeout单位是jiffies
这等待信号通知特定任务的完成或指定的超时时间到期。timeout的单位是jiffies。它不是可中断的。

如果超时,则返回0;
如果完成,则返回正值(剩下的jiffies时间:ret=timeout-runtime)。
举例:

wait_for_completion_timeout (&data_read_done);

wait_for_completion_interruptible

这是等待要发出信号的特定任务的完成。它是可中断的。

int wait_for_completion_interruptible (struct completion * x);

x指向 保持此特定完成的状态
timeout单位是jiffies
如果被中断,则返回-ERESTARTSYS;
如果超时,则返回0;
如果已完成,则返回正数(至少1,或直到超时的剩余数量)。

wait_for_completion_killable

等待等待完成特定任务的信号。它可以被终止信号打断。

int wait_for_completion_killable (struct completion * x);

x指向保持此特定完成的状态
如果被中断,则返回-ERESTARTSYS;
如果超时,则返回0;

wait_for_completion_killable_timeout

这等待信号通知特定任务的完成等待指定的超时时间到期。它可以被终止信号打断。超时最好使用msecs_to_jiffies()usecs_to_jiffies()计算,以使代码在很大程度上保持HZ不变。

long wait_for_completion_killable_timeout (struct completion * x, unsigned long timeout);

x指向 保持此特定完成的状态
timeout单位是jiffies

如果被中断,则返回-ERESTARTSYS;
如果超时,则返回0;
如果已完成,则返回正数(至少1,或直到超时的剩余数量)。

try_wait_for_completion

此函数不会将线程放在等待队列中,而是在需要排队(阻塞)线程时返回false,否则它将消耗一个已发布的完成并返回true。

bool try_wait_for_completion (struct completion * x);

x指向 保持此特定完成的状态
如果无法完成则返回0;
如果成功则返回1。
在IRQ或原子上下文中可以安全地调用此try_wait_for_completion()

唤醒任务

complete

这将唤醒一个线程,等待完成。线程将以排队的顺序被唤醒。
x指向 保持此特定完成的状态
例子:

complete(&data_read_done);

complete_all

这将唤醒等待此特定完成事件的所有线程。

void complete_all (struct completion * x);

x指向 保持此特定完成的状态

检查状态

这是一项测试,以查看完成是否有任何等待。

bool completion_done (struct completion * x);

x指向 保持此特定完成的状态
如果有服务员,则返回0(wait_for_completion正在进行);
如果没有服务员,则返回1。
可以在IRQ或原子上下文中安全地调用该complete_done()

Driver Source Code – Completion in Linux

首先,我将向您解释驱动程序代码的概念。

在此源代码中,我们将在两个地方发送完整的调用。一个来自读取功能,另一个来自驱动程序退出功能。

我创建了一个具有while(1)的线程(wait_function)。该线程将始终等待事件完成。它将一直处于睡眠状态,直到获得completion的调用。当它获得completion的调用时,它将检查条件。如果条件为1,则completion来自读取功能。它是2,则completion来自退出功能。如果完成来自读取功能,它将打印读取计数并再次等待。如果它来自退出函数,它将从线程中退出。

在这里,我添加了两个版本的代码。

  1. 通过静态方法创建的补全
  2. 通过动态方法创建的补全

但是从操作角度来看,两者是相同的。
GitHub

通过静态方法创建的补全

/***************************************************************************//**
*  \file       driver.c
*
*  \details    Simple linux driver (Completion 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/completion.h>                // Required for the completion
 
 
uint32_t read_count = 0;
static struct task_struct *wait_thread;
 
DECLARE_COMPLETION(data_read_done);
 
dev_t dev = 0;
static struct class *dev_class;
static struct cdev etx_cdev;
int completion_flag = 0;
 
static int __init etx_driver_init(void);
static void __exit etx_driver_exit(void);
 
/*************** Driver Functions **********************/
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_for_completion (&data_read_done);
                if(completion_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);
                completion_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");
        completion_flag = 1;
        if(!completion_done (&data_read_done)) {
            complete (&data_read_done);
        }
        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;
        }
 
        //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;
}
 
static void __exit etx_driver_exit(void)
{
        completion_flag = 2;
        if(!completion_done (&data_read_done)) {
            complete (&data_read_done);
        }
        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("A simple device driver - Completion (Static Method)");
MODULE_VERSION("1.23");

通过动态方法完成

/****************************************************************************//**
*  \file       driver.c
*
*  \details    Simple linux driver (Completion 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/completion.h>                // Required for the completion
 
 
uint32_t read_count = 0;
static struct task_struct *wait_thread;
 
struct completion data_read_done;
 
dev_t dev = 0;
static struct class *dev_class;
static struct cdev etx_cdev;
int completion_flag = 0;
 
static int __init etx_driver_init(void);
static void __exit etx_driver_exit(void);
 
/*************** Driver Functions **********************/
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_for_completion (&data_read_done);
                if(completion_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);
                completion_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");
        completion_flag = 1;
        if(!completion_done (&data_read_done)) {
            complete (&data_read_done);
        }
        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;
        }
 
        //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");
 
        //Initializing Completion
        init_completion(&data_read_done);
 
        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;
}
 
static void __exit etx_driver_exit(void)
{
        completion_flag = 2;
        if(!completion_done (&data_read_done)) {
            complete (&data_read_done);
        }
        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("A simple device driver - Completion (Dynamic Method)");
MODULE_VERSION("1.24");

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
rmmod driver # 移除模块
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值