生产者消费者之无锁队列
生产者消费者模型包含4种模式,本文主要讨论1:1,以linux内核2.6.24.4版本kfifo为例。
1:1,
1:n,
n:n,
n:1
kfifo的定义文件:kernel/kfifo.c
kfifo的头文件: include/linux/kfifo.h
1. kfifo概述
kfifo是内核里面的一个First In First Out数据结构,它采用环形循环队列的数据结构来实现;它提供一个无边界的字节流服务,最重要的一点是,它使用并行无锁编程技术,即当它用于只有一个入队线程和一个出队线程的场情时,两个线程可以并发操作,而不需要任何加锁行为,就可以保证kfifo的线程安全。
struct kfifo {
unsigned char *buffer; /* the buffer holding the data */
unsigned int size; /* the size of the allocated buffer */
unsigned int in; /* data is added at offset (in % size) */
unsigned int out; /* data is extracted from off. (out % size) */
spinlock_t *lock; /* protects concurrent modifications */
};
这是kfifo的数据结构,kfifo主要提供了两个操作,__kfifo_put(入队操作)和__kfifo_get(出队操作)。 它的各个数据成员如下:
buffer, 用于存放数据的缓存
size, buffer空间的大小,在初化时,将它向上扩展成2的幂
lock, 如果使用不能保证任何时间最多只有一个读线程和写线程,需要使用该lock实施同步。
in, out, 和buffer一起构成一个循环队列。 in指向buffer中队头,而且out指向buffer中的队尾
2. kfifo_alloc 分配kfifo内存和初始化工作
struct kfifo *kfifo_alloc(unsigned int size, gfp_t gfp_mask, spinlock_t *lock)
{
unsigned char *buffer;
struct kfifo *ret;
/*
* round up to the next power of 2, since our 'let the indices
* wrap' tachnique works only in this case.
*/
if (size & (size - 1)) {
BUG_ON(size > 0x80000000);
size = roundup_pow_of_two(size);
}
buffer = kmalloc(size, gfp_mask);
if (!buffer)
return ERR_PTR(-ENOMEM);
ret = kfifo_init(buffer, size, gfp_mask, lock);
if (IS_ERR(ret))
kfree(buffer);
return ret;
}
kfifo->size的值总是在调用者传进来的size参数的基础上向2的幂扩展,这是内核一贯的做法。这样的好处不言而喻--对kfifo->size取模运算可以转化为与运算,如下:
kfifo->in % kfifo->size 可以转化为 kfifo->in & (kfifo->size – 1)
在kfifo_alloc函数中,使用size & (size – 1)来判断size 是否为2幂,如果条件为真,则表示size不是2的幂,然后调用roundup_pow_of_two将之向上扩展为2的幂。
3. __kfifo_put和__kfifo_get,巧妙的入队和出队操作,无锁并发
__kfifo_put是入队操作,它先将数据放入buffer里面,最后才修改in参数;__kfifo_get是出队操作,它先将数据从buffer中移走,最后才修改out。当只有一个读经程和一个写线程并发操作时,不需要任何额外的锁,就可以确保是线程安全的,也即kfifo使用了无锁编程技术,以提高kernel的并发。
unsigned int __kfifo_put(struct kfifo *fifo,
unsigned char *buffer, unsigned int len)
{
unsigned int l;
len = min(len, fifo->size - fifo->in + fifo->out);
/*
* Ensure that we sample the fifo->out index -before- we
* start putting bytes into the kfifo.
*/
smp_mb();
/* first put the data starting from fifo->in to buffer end */
l = min(len, fifo->size - (fifo->in & (fifo->size - 1)));
memcpy(fifo->buffer + (fifo->in & (fifo->size - 1)), buffer, l);
/* then put the rest (if any) at the beginning of the buffer */
memcpy(fifo->buffer, buffer + l, len - l);
/*
* Ensure that we add the bytes to the kfifo -before-
* we update the fifo->in index.
*/
smp_wmb();
fifo->in += len;
return len;
}
unsigned int __kfifo_get(struct kfifo *fifo,
unsigned char *buffer, unsigned int len)
{
unsigned int l;
len = min(len, fifo->in - fifo->out);
/*
* Ensure that we sample the fifo->in index -before- we
* start removing bytes from the kfifo.
*/
smp_rmb();
/* first get the data from fifo->out until the end of the buffer */
l = min(len, fifo->size - (fifo->out & (fifo->size - 1)));
memcpy(buffer, fifo->buffer + (fifo->out & (fifo->size - 1)), l);
/* then get the rest (if any) from the beginning of the buffer */
memcpy(buffer + l, fifo->buffer, len - l);
/*
* Ensure that we remove the bytes from the kfifo -before-
* we update the fifo->out index.
*/
smp_mb();
fifo->out += len;
return len;
}
生产者消费者之无锁队列相关推荐
- 你应该知道的高性能无锁队列Disruptor
1.何为队列 听到队列相信大家对其并不陌生,在我们现实生活中队列随处可见,去超市结账,你会看见大家都会一排排的站得好好的,等待结账,为什么要站得一排排的,你想象一下大家都没有素质,一窝蜂的上去结账,不 ...
- 基于数组的无锁队列(译)
2019独角兽企业重金招聘Python工程师标准>>> 1 引言 最近对于注重性能的应用程序,我们有了一种能显著提高程序性能的选择:多线程.线程的概念实际上已经存在了很长时间.在过去 ...
- 原理剖析(第 012 篇)Netty之无锁队列MpscUnboundedArrayQueue原理分析
原理剖析(第 012 篇)Netty之无锁队列MpscUnboundedArrayQueue原理分析 - 一.大致介绍 1.了解过netty原理的童鞋,其实应该知道工作线程组的每个子线程都维护了一个任 ...
- 一种高性能无锁队列设计
分布式与多核处理器在共享资源的情况下均要求在线程安全完成提交的任务,在多线程并发处理大量数据任务情况下为解决多生产者多消费者保证任务队列线程安全设计查找到一种高性能无锁队列设计,进行学习.研究. 主要 ...
- 【高并发】多线程之无锁队列|性能优化
队列操作模型 (1)单生产者--单消费者 (2)多生产者--单消费者 (3)单生产者--多消费者 (4)多生产者--多消费者 3.队列数据定长与变长 (1)队列数据定长 (2)队列数据变长 并发无锁处 ...
- CAS无锁队列的实现
文章目录 1. 基本原理 2. 代码实现 2.1 使用链表实现无锁队列 2.2 使用数组实现环形无锁队列 3. ABA 问题及解决 4. 参考资料 1. 基本原理 源于1994年10月发表在国际并行与 ...
- 无锁队列与有锁队列性能比较
最近研究boost的无锁队列,测试了一下性能,发现无锁队列比有锁的还要慢 testqueue.cpp #include <time.h> #include <boost/thread ...
- 无锁队列原理及实现(一)
背景 在进行实际生产多线程开发的时候通常不会直接使用使用锁机制来操作线程间传递的数据,特别是对效率要求很高的场景中.最典型的就是音视频项目或者网络项目.这里先拿网络传输场景举例, 从这篇开始就开始详细 ...
- 原理剖析-Netty之无锁队列
一.大致介绍 1.了解过netty原理的童鞋,其实应该知道工作线程组的每个子线程都维护了一个任务队列: 2.细心的童鞋会发现netty的队列是重写了队列的实现方法,覆盖了父类中的LinkedBlock ...
最新文章
- 2018-3-11 HDFS2.X
- Linux有问必答-如何创建和挂载XFS文件系统
- python编程有用吗-分享8点超级有用的Python编程建议
- mssql 计划怎每隔n秒_前端:调你一个接口6秒还配资深工程师?后端:有24部分需要处理!...
- 什么事计算机事实性知识,《人工智能》复习要点
- [mybatis]映射文件_select_resultMap_关联查询_association分步查询延迟加载
- c++ 指针拼接字符串_字符串拼接+和concat的区别
- 图书馆管理系统——超期付款
- 无任何歧视!任正非:愿意把5G技术授权给一家美国公司
- LNMP编译安装基于centos7.2
- 涨薪慢,该不该跳槽?
- Tornado引入静态css、js文件
- 《剑指offer》面试题——把数组排成最小的数
- textarea高度自适应且不出现滚动条
- PAT-求特殊方程的正整数解(简单编程题)
- 思科三层交换机开启ipv6路由功能_思科三层交换机路由功能配置教程
- mysql例子 restful_restful例子问题
- 【GTASA】如何解锁Locked的DFF模型
- Axure教程:用中继器制作调查问卷/考试试卷
- Kafka多租户(配额)管理
热门文章
- linux下的文件可以分为哪五种类型,LINUX系统文件类型分类
- 2018.12.10 第4题:定义两个类,描述如下: [必做题] 4.1定义一个人类Person: 4.1.1定义一个方法sayHello(),可以向对方发出问候语“hello,my name is
- java的无参构造函数_Java 无参数构造函数的应用
- IBM P系列日常管理——服务器的故障分析和处理
- 洛谷 PT2 First Step (ファーストステップ)
- iPhone键盘改变颜色
- SVN设置提交忽略某些文件或文件夹
- stm32学习第十四天
- Apollo基本使用
- NDCG归一化折损累积增益