linux驱动程序中的并发控制-8(完成量(completion))-50(代码片段)

杨斌并 杨斌并     2022-12-07     727

关键词:

完成量(completion)

  • 完成量用于一个执行单元等待另一个执行单元执行完成某项工作。也就是说,如果在执行某段代码之前必须要执行另一段代码,就要使用完成量。

完成量(completion)使用

  1. 定义完成量
  • 结构体(#include <linux/completion.h>)
static struct completion my_completion;
struct completion 
	unsigned int done;
	wait_queue_head_t wait;
;

其中done变量非常重要,该变量用于标识任务已经完成的次数。初始化后该变量值为0。后面会介绍如何使done变量加1或减1。

  1. 初始化完成量
init_completion(&my_completion);
//定义和初始化
DECLARE_COMPLETION(my_completion) 
  1. 等待完成量
  • wait_for_completion函数会根据completion. done变量的值决定是否等待一个完成量。该函数的定义如下:
void wait_for_completion(struct completion *c) ;
  • 如果completion.done 变量的值为0,wait form completion会通过阻塞的方式等待一个completion的完成。并且每调用一次 wait_for_completion 函数completion.done 变量的值会减1,直到为0时被阻塞,继续等待completion的完成。
  • 一个等待completion完成的函数是wait_ for_ completion interrupbible, 该函数的定义如下:
int wait_for_completion_interruptible (struct completion *c) ;
  • wait_for_completion和wait_for_completion_interrupbible 的区别是wait_for_completion函数在等待completion完成时不能被中断打断,而wait_for_ completion_inferrupbible 函数在等待completion完成时可以被中断打断。
  1. 唤醒完成量
extern void complete(struct completion *);
extern void complete_all(struct completion *);

complete函数用于唤醒一个 等待的执行单元,complete_all 函数用于唤醒所有等待统一完成量的执行单元。调用compete 函数会使completion.done 变量的值加1, 而complete_all 函数会将completion.done变量值设为int的最大值,也就是2147483647。这也是为什么使用complete_all 函数可以唤醒所有completion的原因。只要被completion阻塞的执行单元不大于2147483647个,就可以都被唤醒。


实例

  • completion_test.c
//
//  completion_test.c
//  seqlock_demo
//
//  Created by lianfei on 2021/7/13.
//


#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>

#define DEVICE_RCU_NAME "completion"

static struct completion my_completion;
static int completion_type = 0;

static ssize_t demo_read(struct file *file, char __user * buf, size_t count, loff_t *ppos)
    
    printk("read start completion.done: %d\\n", my_completion.done);
    wait_for_completion(&my_completion);
    printk("read end completion.done: %d\\n", my_completion.done);
    
    return 0;



static ssize_t demo_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
    printk("write start completion.done: %d\\n", my_completion.done);
    if (completion_type == 0) 
        complete(&my_completion);
    else if (completion_type == 1)
        complete_all(&my_completion);
    
    printk("write end completion.done:%d\\n", my_completion.done);
    return count;


static int demo_release(struct inode *node, struct file *file)
    return 0;


static int demo_open(struct inode *node, struct file *file)
    return 0;


static struct file_operations dev_fops=
    .owner = THIS_MODULE,
    .open = demo_open,
    .release = demo_release,
    .read = demo_read,
    .write = demo_write
;

static struct miscdevice misc=
    .minor = MISC_DYNAMIC_MINOR,
    .name = DEVICE_RCU_NAME,
    .fops = &dev_fops
;


static int demo_init(void)

    int ret=misc_register(&misc);
    if(ret < 0 )
        printk("atomic_init is error\\n");
        return -1;
    
    printk("demo_init_success\\n");
    init_completion(&my_completion);
    return ret;


static void demo_exit(void)
    printk("ademo_exit_success\\n");
    misc_deregister(&misc);


module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL");
module_param(completion_type, int, S_IRUGO|S_IWUSR);
MODULE_AUTHOR("binbing.Yang");
  • 测试脚本
  • completion_test.sh
#!/system/bin/sh
cat /dev/completion
echo data > /dev/completion

通过completion_type 模块参数可以设置completion驱动使用complete还是complete_all 函数唤醒完成量。当读取/dev/completion设备文件时,会调用demo_ read 函数,在该函数中调用了wait_for_completion函数等待,complete被唤醒。如果首先读取 /dev/completion 设备文件,demo_read函数将被阻塞,向/dev/completion 设备文件写入数据时,会调用demo_read 函数,在该函数中会根据completion_type模块参数的值使用complete或complete_all唤醒完成量。

linux驱动程序中的并发控制-8(完成量(completion))-50(代码片段)

完成量(completion)完成量用于一个执行单元等待另一个执行单元执行完成某项工作。也就是说,如果在执行某段代码之前必须要执行另一段代码,就要使用完成量。完成量(completion)使用定义完成量结构... 查看详情

设备驱动中的并发控制

在为操作系统编写驱动设备时,因为涉及到中断、多任务和多处理器SMP的处理,所以内核提供了诸如中断屏蔽、原子操作、信号量、完成量等几种并发控制机制,对公用资源进行保护。下文将分别予以阐述。0、中断中断屏蔽使... 查看详情

并发控制

目录自旋锁完成量自旋锁 《Linux设备驱动开发详解》=>7.5自旋锁spinlock:在rtlinux(配置了PREEMPT_RT)的时候可能会被抢占         (实际底层可能是使用支持PI(优先级翻转)的mutex)。raw_spinlock:即便是配置了PREEMPT_RT也要... 查看详情

linux设备驱动的并发控制学习笔记(代码片段)

文章目录并发和竞态编译乱序和执行乱序并发控制机制中断屏蔽原子操作整型原子操作位原子操作自旋锁自旋锁的使用读写自旋锁顺序锁读-复制-更新信号量互斥体完成量并发和竞态并发:多个执行单元同时、并行被执行。... 查看详情

linux驱动程序中的并发控制-4(顺序自旋锁)-46(代码片段)

顺序自旋锁顺序锁与读写自旋锁类似,只是为写锁赋予了更高的权限。在读写自旋锁中,读锁和写锁的优先级是相同的。当读锁获取读自旋锁时,写锁必须等待,直到临界区的代码执行完成,并释放读自旋锁... 查看详情

linux驱动程序中的并发控制-4(顺序自旋锁)-46(代码片段)

顺序自旋锁顺序锁与读写自旋锁类似,只是为写锁赋予了更高的权限。在读写自旋锁中,读锁和写锁的优先级是相同的。当读锁获取读自旋锁时,写锁必须等待,直到临界区的代码执行完成,并释放读自旋锁... 查看详情

linux设备驱动中的并发

参考技术A并发就是多个执行单元或多个进程并行执行,而这多个执行单元对资源进行共享,比如访问同一个变量或同一个硬件资源,这个时候就很容易出现竞态(说简单点就是竞争同一个”女朋友”)。为了处理并发带来的问题,... 查看详情

第十一章

...修改共享数据。为了达到这些目的,就需要本章要讨论的并发控制技术。这些技术主要包括原子操作、自旋锁、RCU、信号量、互斥体和完成量。本章还为每一种并发控制技术配有完整的示例, 查看详情

linux驱动程序中的并发控制-7(互斥体(mutex))-49(代码片段)

互斥体(mutex)互斥体(mutex)使用定义互斥体(#include<linux/mutex.h>)结构体structmutex /*1:unlocked,0:locked,negative:locked,possiblewaiters*/ atomic_t count; spinlock_t wait_lock; st 查看详情

linux驱动程序中的并发控制-7(互斥体(mutex))-49(代码片段)

互斥体(mutex)互斥体(mutex)使用定义互斥体(#include<linux/mutex.h>)结构体structmutex /*1:unlocked,0:locked,negative:locked,possiblewaiters*/ atomic_t count; spinlock_t wait_lock; str 查看详情

linux驱动程序中的并发控制(原子操作)-43(代码片段)

原子操作整型的原子操作使对整型的int的操作变成原子操作,要依靠一个数据类型:atomic_t。此结构体定义在include/linux/types.h文件中,定义如下:typedefstruct intcounter;atomic_t;相关的api#include<asm/atomic.h>函数描述ATO... 查看详情

linux驱动程序中的并发控制-1(原子操作)-43(代码片段)

原子操作整型的原子操作使对整型的int的操作变成原子操作,要依靠一个数据类型:atomic_t。此结构体定义在include/linux/types.h文件中,定义如下:typedefstruct intcounter;atomic_t;相关的api#include<asm/atomic.h>函数描述ATO... 查看详情

linux驱动程序中的并发控制(自旋锁)-44(代码片段)

自旋锁(spinlock)简介原子锁和自旋锁的使用范围原子操作是一种很好的避免竞态的方式,使用非常简单。但在某些方面却显得过于简单。例如,有很多数据需要被格式化,被添加到某些数据结构中,然后... 查看详情

linux驱动程序中的并发控制-2(自旋锁)-44(代码片段)

自旋锁(spinlock)简介原子锁和自旋锁的使用范围原子操作是一种很好的避免竞态的方式,使用非常简单。但在某些方面却显得过于简单。例如,有很多数据需要被格式化,被添加到某些数据结构中,然后... 查看详情

linux驱动之并发与竞争(代码片段)

...同一片内存区域,这些任务可能会相互覆盖这段内存中的数据,造成内存数据混乱。针对这个问题必须要做处理,严重的话可能会导致系统崩溃。linux存在以下并发访问:①、多线程并发访问,Linux是多任务(线程)的... 查看详情

linux驱动程序中的并发控制-3(读写自旋锁)-45(代码片段)

读写自旋锁自旋锁不管读写,都只允许同时只有一个执行单元可以获取自旋锁,即便有多个单元同时读取临界区资源也会被锁住。读写自旋锁,将临界区的读写操作分开,多个执行单元可以同时获取一个读自旋锁&... 查看详情

linux驱动程序中的并发控制-5(信号量(semaphore))-47(代码片段)

信号量(semaphore)信号量是用于保护临界区的一种常用方法,它的使用方式与自旋锁类似。与自旋锁相同,只有得到信号量的进程才能执行临界区代码。但与自旋锁不同的是,在未获取信号量时,进程不会... 查看详情

linux驱动程序中的并发控制-5(信号量(semaphore))-47(代码片段)

信号量(semaphore)信号量是用于保护临界区的一种常用方法,它的使用方式与自旋锁类似。与自旋锁相同,只有得到信号量的进程才能执行临界区代码。但与自旋锁不同的是,在未获取信号量时,进程不会... 查看详情