优秀数据结构学习 - 共享内存无锁队列的实现(二)

优秀数据结构学习 - 共享内存无锁队列的实现(二)

1 关键技术

操作系统提供的进程间通信机制有文件、socket、消息队列、管道、共享内存等。其中,共享内存是最快的IPC机制[6]。

共享内存映射到进程空间后,数据可以直接从共享内存进行读写,不需要执行系统调用进行数据的传输。因此,避免了其它进程间通信机制必须的用户态/内核态切换以及用户空间与内核空间的数据拷贝。

由于消息队不需支持跨主机通信,所以可以采用共享内存进行进程间的通信。

消息队列需要支持多个写者,在多个写者同时进行写操作时,会产生并发操作。处理并发操作,通常的解决方案是使用互斥锁。使用互斥锁简单方便,但有一定的系统开销。在我们的测试环境下,单次锁/解锁操作耗时大概为20纳秒。多个线程互斥操作时,时间开销大大增加。同样的测试条件,当两个线程并发操作时,单次锁/解锁操作的平均时延为320纳秒。

如果并发操作能限制在一个机器字节内,可以使用CPU提供的CAS指令,即’Compare& Swap’原子操作,进行进程间的互斥操作。

相较于使用互斥锁进行多进程间的互斥操作,使用CAS指令的程序“临界区”更小,只有一个字节。当多个写者同时更新“临界区”时,只有一个写者成功,其它写者需要重复操作并检查结果。这样避免了进程锁起时休眠,以及锁操作带来的开销。最大程度的降低响应时延。

无锁程序的关键在于程序设计时,将程序的”临界区”设置为一个机器字节的变量,进而可以使用CAS指令进行原子操作。

1.2.1 消息队列多写者的无锁操作

在消息队列中,数组和数组的头尾head、tail指针是多进程访问的临界区。

只考虑写者,操作临界区为向数组写入数据、tail指针的更新:

critical section begin

write to A[tail]

tail++

critical section end

减少临界区内容,在临界区只对tail指针进行修改:写者竞争获取到tail针后,再写入数据:

critical section begin

s = tail

tail++

critical section end

write to A[s]

现在,临界区只有tail一个变量,使用CAS指令代替互斥锁:

s = tail

do{

next tail = s+1;

ret = CAS(&tail, s, next_tail);

if(s == ret)

break;

s = ret;

}while(1);

write to A[s];

1.2.2 队列读操作

由于只有一个读者,因此不需考虑head指针并发操作的问题。但由于写者先更新tail指针内容,后写入数据,读者在读数据前需要先判断数据是否有效。

消息队列的消息格式如下:

struct {

volatile unsigned int data_len;

char data[0];

};

写者写入数据前,data_len为0,写入数据后,更新data_len内容。此时,读者才开始读数据。读者读取数据后,重置data_len为0;这样,下次写者写入该位置数据完成前,读者不会提前读取。

2 消息队列设计

消息队列不是作为独立的服务,而是设计成库的形式提供使用。应用程序调用消息队列库的接口,编译时将库文件链接到目标文件即可。

按照功能划分,共享内存消息队列划分为消息队列、消息队列创建、消息队列销毁、数据读取、数据写入、消息队列查询等模块,各模块对应的功能如下:

消息队列: 提供基础数据结构,缓存写入的数据,供其他模块读取或者查询。

消息队列创建: 创建指定键值、大小和容量的共享内存消息队列。

消息队列销毁: 销毁指定键值的共享内存消息队列,释放共享内存。

数据读取: 读取共享内存消息队列中的数据。

数据写入: 向共享内存消息队列中写入数据。

消息队列查询: 查询共享内存消息队列的使用情况。

2.1.1 总体组成

共享内存消息队列的组成如下图,其中消息队列由消息队列创建模块创建,供数据读写模块、消息队列查询模块使用,最终被消息队列销毁模块销毁释放。

2.1.2 消息队列组成

消息队列是一个基于共享内存的环形队列,在开辟的一块连续的共享内存中,保存消息队列相关信息,缓存写入的数据。它在内存中的结构定义:

消息队列头包含队列大小、消息记录大小,head、tail位置指针,用于读写同步的条件变量和锁等控制信息。环形队列分别使用首指针head和尾指针tail标记队列的头和尾,读者从head指向的位置读取数据,写者向tail指向的位置写入数据。

2.2.1 创建和销毁

消息队列应该在应用进程启动前创建,在应用进程退出后再销毁。创建和销毁由独立的程序调用创建和销毁模块进行操作。

创建消息队列过程主要包括:

1. 根据参数确定消息长度、个数,计算队列缓存大小

2. 申请共享内存

3. 初始化消息队列头部控制信息,初始化队列缓存

销毁消息队列过程主要包括:

1. 销毁消息队列头部控制信息

2. 释放共享内存其中控制信息主要包括读者、写者同步需要的条件变量和互斥锁。

2.2.2 数据写入

消息队列支持多个写者同时写操作,多个写者对队列的竞争写由CAS操作完成。写操作的过程如下:

1. 检查环形队列空闲数量是否大于临界值

2. 如果是,执行3;如果否,超时等待,继续执行1

3. 获取当前tail值,new_tail为tail+1

4. 通过CAS指令,使用new_tail更新tail

5. 如果成功,根据new_tail位置写入数据,然后更新该位置消息的data_len为实际数据长度;如果不成功,跳到1继续执行

6. 如果有读者在等待,通知读者

值得注意的是,写者先更新tail指针,后写入数据。这样才能保证多个写者通过CAS进行互斥操作。但是tail指针更新后,不保证数据已经更新完成。读者在读数据时需要根据data_len的值判断数据是否完整。

2.2.3 数据读取

消息队列只有一个读者,逻辑比较简单,读操作过程如下:

1. 检查环形队列是否有数据

2. 如果是,head自加1;如果否,超时等待,执行1

3. 检查环形队列head位置的数据data_len是否为0

4. 如果是,超时等待,若超过500毫秒data_len仍为0,认为写者异常,跳过该位置,执行1;如果否,读取数据

5. 更新该位置data_len为0

6. 如果有写者在等待,通知写者

2.2.4 读写同步

读者和写者使用条件变量进行同步。条件变量保存在消息队列头部信息中。只有当写者因没有空闲位置进入等待状态时,读者读取数据后才会发送条件变量信号进行通知。同样,只有当读者因没有数据而进入等待状态时,写者写入数据后才发送条件变量信号进行通知。

详细介绍:https://mp.weixin.qq.com/s/RqHsX3NIZ4_BS8O30KWYhQ

优秀数据结构学习 - 共享内存无锁队列的实现(二)相关教程

达梦数据库学习之ODBC

达梦数据库学习之ODBC 这里的安装包都是在网上自行寻找。 再这里分享一下链接:https://pan.baidu.com/s/11eWq2lRmnfHvERPD1xTqOA 提取码:gf0w 一、yum安装 1、YUM 安装UnixODBC库: yum install -y unixODBC.x86_64 unixODBC-devel.x86_64 2、vim /etc/odb

数据结构32:选择排序

数据结构32:选择排序 选择排序Selection Sort 选择排序对冒泡排序进行了改进,保留了其基本的多趟对比的思路,每趟都使当前最大项就位。 但是选择排序对交换进行了削弱,每趟仅进行一次交换,记录最大项的所在位置,最后再跟本趟最后一项进行交换。 选择排序

【亡羊补牢】挑战数据结构与算法 第48期 LeetCode 104. 二叉树的

【亡羊补牢】挑战数据结构与算法 第48期 LeetCode 104. 二叉树的最大深度(二叉树) 仰望星空的人,不应该被嘲笑 给定一个二叉树,找出其最大深度。 二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。 说明: 叶子节点是指没有子节点的节点。 示例:

数据结构31:冒泡排序

数据结构31:冒泡排序 目录 一、冒泡排序Bubble Sort 二、算法分析 三、性能改进 一、冒泡排序Bubble Sort 冒泡排序的算法思路在于对无序表进行多趟比较交换,每趟包括了多次两两相邻比较,并将逆序的数据项交换位置,最终能将本趟的最大项就位。经过n-1趟比

数据结构(10)_递归

数据结构(10)_递归 1.递归的思想 递归时一种数学上分而自治的思想,将原有问题分解为规模较小的问题进行处理。 1.分解后的问题与原问题的类型完全相同,但规模较小; 2.通过小规模问题的解,能够轻易的求得原问题的解。 问题的分解是有限的(递归不能无限进行

数据结构(09)_字符串类的实现

数据结构(09)_字符串类的实现 1.字符串类的创建(上) C语言不支持真正意义上的字符串,使用字符指针和字符数组实现字符串存储,使用C库函数实现字符串操作。 C++为了兼容C语言,也不支持原生的字符串类型,但可以通过自定义类类型完成字符串类型的定义。 继承

数据结构(06)_栈

数据结构(06)_栈 1.栈的设计和实现 概念: 栈是一种特殊的线性表,仅能在线性表的一端(栈顶)进行操作。 栈的特性: 后进先出(last in first out) 栈的基本操作: 创建栈(stack()); 销毁栈(~stack()); 清空栈(clear()) 进栈(push()); 出栈(pop()

数据结构(07)_队列

数据结构(07)_队列 1. .队列的概念和实现 队列是一种特殊的线性表,仅能在线性表的两端进行操作。 -队头(front)取出数据元素的一端; -队尾(rear)插入数据元素的一端。 队列的特性: 队列的常用操作: 创建和销毁;出队和入队;清空队列;获取队首元素;

Linux无锁共享内存,优秀数据结构学习 - 共享内存无锁队列的实现(二)相关推荐

  1. ajax无刷新评论的思路,ajax学习——ajax版无刷新评论(数据库)

    //Comment.htm 无刷新评论 type="text/javascript"> //加载评论 $(function() { $.post("GetComme ...

  2. linux学习---基于内存的IPC(共享内存,信号量数组,消息队列)

    常用的IPC分为两个类别,一是基于文件,而是基于内存 基于文件的分别有匿名管道,有名管道,普通的文件共享,socket文件 如果要看基于文件的IPC,请参考:http://blog.csdn.net/ ...

  3. Linux进程通信的四种方式——共享内存、信号量、无名管道、消息队列|实验、代码、分析、总结

    Linux进程通信的四种方式--共享内存.信号量.无名管道.消息队列|实验.代码.分析.总结 每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须 ...

  4. Linux访问其他进程空间,Linux环境进程间通信系列(五):共享内存

    共享内存可以说是最有用的进程间通信方式,也是最快的 IPC 形式.两个不同进程 A . B 共享内存的意思是,同一块物理内存被映射到进程 A . B 各自的进程地址空间.进程 A 可以即时看到进程 B ...

  5. Linux环境进程间通信系列(五):共享内存

    共享内存(上) 共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式.两个不同进程A.B共享内存的意思是,同一块物理内存被映射到进程A.B各自的进程地址空间.进程A可以即时看到进程B对共享内存 ...

  6. Linux 3.进程间通信(shmget shmat shmdt shmctl 共享内存、signal signaction sigqueue 信号、semget semctl semop 信号量)

    Linux 3.进程间通信(IPC) 共享内存 共享内存的接口指令 shmget 创建获取获取共享内存 shmat 映射:连接共享内存到当前进程的地址空间 shmdt 断开与共享内存的连接 shmct ...

  7. 《深入理解LINUX内存管理》学习笔记(一)

    引子 为什么要写这个笔记: 1,这本书的中文版翻译了太垃圾,没法阅读.阅读英文原版,可以很好的理解作者的思路.作此笔记备忘 2,一直以来学习LINUX kernel的知识缺乏系统化,借对这本书的学习, ...

  8. VoltDB介绍——本质:数据保存在内存,充分利用CPU,单线程去锁,底层数据结构未知...

    转自:http://blog.csdn.net/ransom0512/article/details/50440316 简介 VoltDB数据库是一个分布式,可扩展,shared-nothing的内存 ...

  9. Linux 进程间通讯(IPC)方式 ------- 共享内存

    Linux 进程间通讯(IPC)方式有以下几种: 1->管道(pipe)和有名管道(fifo). 2->消息队列 3->共享内存 4->信号量 5->信号(signal) ...

最新文章

  1. 2019.01.07|区块链技术头条
  2. Mysql中的增删改查操作
  3. BZOJ 4720: [Noip2016]换教室
  4. 德勤预判:2022技术七大趋势
  5. 10.傅里叶变换——傅里叶变换、计算傅里叶变换_3
  6. 深度学习2.0-普通BP神经网络
  7. Windows10 安装 protobuf
  8. 命令行快速访问远程目录
  9. verifycode.php,verifycode.php
  10. 自己写的一个简单JAVA网络通讯录
  11. 网络编程在线英英词典之历史查询模块(六)
  12. java 时间轮_惊艳的时间轮定时器
  13. windows共享时出现“指定网络名不再可用”解决办法
  14. Return value (126) was not iterable.
  15. 2、树莓派声卡设置和alsactl命令的使用
  16. tools: USB、MiniUSB、MicroUSB接线
  17. vue 文字无缝滚动_vue文字横向滚动公告
  18. macOS Big Sur 11.6 (20G165) 虚拟机 IOS 镜像
  19. 利用软路由,轻松实现宽带叠加,已达到千兆网速的效果
  20. 安装mysql数据库

热门文章

  1. Unity 实战【360VR 看房】
  2. Flask处理高并发
  3. 矩阵的特征分解(推导+手算+python计算+对称矩阵的特征分解性质)
  4. HTML,你们都怎么读?
  5. 日常(更新至2019.6.27)
  6. python中float函数作用_解析要在Python中浮动的字符串(float()函数)
  7. [CKA]考试之K8s 版本升级
  8. geoserver安装及跨域问题解决方案
  9. vue跨域问题解决方法
  10. 【云原生Docker容器下的灯塔资产收集】