共享内存在进程间应用

一、共享内存

共享内存是指在内存空间中开辟出一段空间供多个进程使用,它是一种跨进程通信的重要手段。共享内存在多进程开发中应用非常多,特别是在跨进程间大数据量通信时,几乎是必备的选择。工程实践中,安卓的framework中就用到了它,当然其它很多知名的软件都有应用,有兴趣的可以自己查找看看。
Linux和Windows中共享内存的处理原理基本是相同的,但方式却有不同。本文只分析Linux下的共享内存,对Windows上的共享内存感兴趣的可以自己查找相关资料。
在Linux下,处理共享内存有两种方式:即传统的system V和Posix库两种形式。

二、共享内存在进程中使用中的同步锁

在进程通信中,最常遇到的就是两个问题,一个数据的读写操作,另外一个就是读写的同步问题。前者是基础的功能,这里重点分析一下同步问题。在实际的工程开发,在进程的锁选择中下意识的就选择了std::mutex。这和早年学习VC中的有名Mutex的影响非常大,在后期随着技术的演进,同时,多进程编程的实际工程少之又少,这也导致了很多问题。基本上的框架如下:

struct SmData final {bool flag = false;std::mutex mt;std::array<int, 100> buffer = {0};
};struct MemData final {// WorkID id;SmData buf[2] = {0};
};

整体的测试流程是好的,但在测试时发现锁无效,两个进程中发送进程从共享内存中拿到mt锁住后,另外的接收进程也能从共享内存拿到锁。测试是好多次都是如此,查询网上得到的结果是std::mutex不可以用在进程间。查看一下其实现的源码:

  /// The standard mutex type.class mutex : private __mutex_base{public:typedef __native_type* 			native_handle_type;#ifdef __GTHREAD_MUTEX_INITconstexpr
#endifmutex() noexcept = default;~mutex() = default;mutex(const mutex&) = delete;mutex& operator=(const mutex&) = delete;
......native_handle_typenative_handle() noexcept{ return &_M_mutex; }};

也没有发现特别的定义。于是便换用了pthread_mutex_t,并将属性设置为PTHREAD_PROCESS_SHARED(注意,只在一个进程中设置即可,此处是在写入进程中设置,另外一个读进程只管操作即可 ),测试成功。然后重新封装了pthread_mutex_t的相关RAII类。
可仍然没有放弃对std::mutex的测试,采用了更准确的测试方式,发现标准库的std::mutex也可以,真的让人没办法,应该是前面的测试方法有问题,产生了误解。改了就改了吧,不改回去了。

POSIX的共享内存示例可以 参看下面的网址:
https://www.bo-yang.net/2016/07/27/shared-memory-ring-buffer#shm_ring_buffer
很详细,说明也非常到位,就不再赘述。这里只说明,这种方式和创建一个内存映射类似,会在应用程序的指定位置生成一个文件,而且这个文件会随着 开辟空间的增大而增大,这也是一个需要重视的问题。不过,也没有太大影响,毕竟硬盘的大小相对于内存来说不是一个量级的存在。

三、两个例程:

下面是两个反复测试的例程(含STL和Posix两种Mutex),稍微有点乱:
下面是写共享内存的:

//send
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <mutex>
namespace mem
{
template <typename Mutex_> class lock_guard final {
public://lock_guard()=default;lock_guard(Mutex_ *mt) : mt_(mt) { pthread_mutex_lock(mt_); }~lock_guard() { pthread_mutex_unlock(mt_); }public:lock_guard(const lock_guard &) = delete;lock_guard(lock_guard &&) = delete;lock_guard &operator=(const lock_guard &) = delete;lock_guard &operator=(lock_guard &&) = delete;private:Mutex_ *mt_;
};
}
struct LockTest
{
int d;
std::mutex mt;
//pthread_mutex_t mt;
char buf[1024];
};
struct LockArr
{
LockTest lt[6];
};
LockArr *tmp = NULL;
std::mutex* init_shm(const key_t key)
{int shmid = shmget(key, sizeof(LockArr), 0666 | IPC_CREAT);if (shmid == -1){return NULL;}void* shm_addr = shmat(shmid, NULL, 0);if (shm_addr == (void *) -1){return NULL;}//pthread_mutexattr_t mutex_attr;//pthread_mutexattr_init(      &mutex_attr);//pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED);//pthread_mutexattr_setrobust( &mutex_attr, PTHREAD_MUTEX_ROBUST);tmp = (LockArr *)shm_addr;std::mutex* _mutex = &tmp->lt[0].mt;//pthread_mutex_t* _mutex = &tmp->lt[0].mt;//pthread_mutex_init(_mutex,&mutex_attr);return _mutex;
}
#define PKEY 20181220
//process
std::mutex* pMutex = NULL;
void writemem(const unsigned char* data)
{if (pMutex == NULL){pMutex = init_shm(PKEY);}std::lock_guard<std::mutex> l(*pMutex);//mem::lock_guard<pthread_mutex_t> pmt{pMutex};//pthread_mutex_lock(pMutex);//do somethingtmp->lt[0].buf[0] = 1;tmp->lt[0].buf[1] = 3;tmp->lt[0].buf[2] = 6;printf("lock successed--------------ooooooo--------------\n");usleep(10 * 1000 * 1000);//pthread_mutex_unlock(pMutex);
}
int main()
{unsigned char buf[1024] = {0};writemem(buf);return 0;
}

下面是读共享内存:

//recv
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <iostream>
#include <mutex>
namespace mem{
template <typename Mutex_> class lock_guard final {
public://lock_guard()=default;lock_guard(Mutex_ *mt) : mt_(mt) { pthread_mutex_lock(mt_); }~lock_guard() { pthread_mutex_unlock(mt_); }public:lock_guard(const lock_guard &) = delete;lock_guard(lock_guard &&) = delete;lock_guard &operator=(const lock_guard &) = delete;lock_guard &operator=(lock_guard &&) = delete;private:Mutex_ *mt_;
};
}
struct LockTest
{
int d;
std::mutex mt;
//pthread_mutex_t mt;
char buf[1024];
};
struct LockArr
{
LockTest lt[6];
};
LockArr *tmp = NULL;/*pthread_mutex_t* */ std::mutex *init_shm(const key_t  key)
{int shmid = shmget( key, sizeof(LockArr), 0666 | IPC_CREAT);if (shmid == -1){return NULL;}void* shm_addr = shmat(shmid, NULL, 0);if (shm_addr == (void *) -1){return NULL;}//pthread_mutexattr_t mutex_attr;//pthread_mutexattr_init(      &mutex_attr);//pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED);//pthread_mutexattr_setrobust( &mutex_attr, PTHREAD_MUTEX_ROBUST);tmp = (LockArr *)shm_addr;//pthread_mutex_t* _mutex  = &tmp->lt[0].mt;std::mutex* _mutex  = &tmp->lt[0].mt;//pthread_mutex_init(_mutex,&mutex_attr);return _mutex;
}
#define PKEY 20181220
//process
std::mutex* pMutex = NULL;
void readmem(const unsigned char* data)
{if (pMutex == NULL){pMutex = init_shm(PKEY);}//lock_guard<pthread_mutex_t>(&pBuf->mt)//mem::lock_guard<pthread_mutex_t> pmt(pMutex);std::lock_guard<std::mutex> l(*pMutex);//pthread_mutex_lock(pMutex);//do somethingstd::cout<<tmp->lt[0].buf[0]<<tmp->lt[0].buf[1]<<tmp->lt[0].buf[2]<<std::endl;printf("lock successed=======99999================\n");usleep(3 * 1000 * 1000);//pthread_mutex_unlock(pMutex);
}
int main()
{unsigned char buf[1024] = {0};readmem(buf);return 0;
}

目前这是用的标准库的std::mutex,如果将pthread_mutex_t放开并注释掉标准库的,也是没有问题的,但是得自己封装一个锁控制互斥体。
这里顺带说一个问题,看下面的代码:

//lock_guard<pthread_mutex_t>(&pBuf->mt);//这行代码OK
//lock_guard<pthread_mutex_t>(pMutex);//编译有问题
//lock_guard()=default;

上面两行代码都被注释了,第二行代码引入了下面第三行代码的出现(虽然增加第三行代码后可以 编译通过),但它们是有问题的,之所以说有问题而不说错误,是因为从形式上讲编译器认为是正确的,但实际达不到目的。编译器报得错误和警告是:

error:candidate expects 1 argument, 0 provided
warning:parentheses were disambiguated as redundant parentheses around declaration of variable named

这引出一个基础的问题,声明一个匿名类对象时,如果使用一个具体的值来定义一个类,则没问题,如果是用一个同样值的变量来定义,编译器就无法确定此处是什么 情况(是类对象定义还是函数声明),从而编译错误提出警告(如果后面级联调用对象内的变量或者函数同样没问题,编译器可以确定其为类对象定义)。解决的方法是要么直接采用新标准下的大括号即lock_guard<pthread_mutex_t>{pMutex},要么如正常开发增加一个变量定义如正文中的代码所示。
共享内存中还有两个收尾的函数:

//共享内存从当前进程中分离,相当于shmat函数的反操作
int shmdt(const void *shmaddr);
//删除共享内存,这个其实有很多种用法,看command的标志
int shmctl(int shm_id, int command, struct shmid_ds *buf);

更细节的可以查看相关资料。

四、总结

在新的C++标准下,提供了很多新的锁,比如scoped_lock、shared_lock(对应的互斥体: timed_mutex、recursive_mutex、recursive_timed_mutex、shared_mutex、shared_timed_mutex)。还是要好好的看一看相关的锁的应用和跨进程中的使用的问题,没有实践就没有发言权。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/6204.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

喜报|英码科技联合广师大荣获“智芯杯”AI芯片应用创新创业大赛两大奖项

7月15日&#xff0c;由中国仪器仪表学会主办的全国首届“智芯杯”AI芯片应用创新创业大赛总决赛暨颁奖典礼圆满结束&#xff0c;英码科技联合广东技术师范大学设计开发的“AI视觉&#xff0c;让工厂建设更智慧”和“基于AI的智慧校园无感考勤系统”创新项目均荣获三等奖。 ​ 自…

springcloudAlibaba之nacos集群部署和nginx负载均衡

1.环境准备 nacos server安装包&#xff1a;https://github.com/alibaba/nacos nginx安装包&#xff1a;https://nginx.org/en/download.html 2、nacos配置 将下载好的nacos-server的压缩包解压好以后&#xff0c;复制出N份&#xff08;这里取决于你集群的数量&#xff09;&…

设计模式之模板方法模式

例子&#xff1a;登陆&#xff08;普通用户&#xff0c;工作人员&#xff09; 没有使用设计模式实现用户登陆 package com.tao.YanMoDesignPattern.template.notPattern;/*** Author Mi_Tao* Date 2023/7/22* Description* Version 1.0**/ public class LoginModel {private …

Grafana中table的使用技巧

将多个指标数据显示在同一个Table中&#xff0c;需要用到Transform功能&#xff0c;利用Transform功能可以将数据进行处理只显示想要的数据&#xff1a;

【VTK】VTK 让小球动起来,在 Windows 上使用 Visual Studio 配合 Qt 构建 VTK

知识不是单独的&#xff0c;一定是成体系的。更多我的个人总结和相关经验可查阅这个专栏&#xff1a;Visual Studio。 文章目录 版本环境A.uiA.hA.cppRef. 本文主要目的是在 Qt 界面中&#xff0c;显示出来使用 VTK 构建的小球&#xff0c;并让小球能够动起来。同时为了方便对比…

快手营销活动面试

衡量平台业务结果 请求&#xff0c;下发&#xff0c;曝光&#xff0c;点击&#xff0c;点击率&#xff0c;消耗系统QPS&#xff1f; 集群&#xff1a;高峰期8万&#xff0c;平峰期5万单机最大多少&#xff1f;性能瓶颈&#xff1f; 平峰期&#xff1a;300高峰期&#xff1a;500…

【C应用】测试CPU架构是大端还是小端模式

【C应用】测试CPU架构是大端还是小端模式 1、背景2、检测大小端1、背景 大端模式、小端模式是字节序里面的概念,即大端字节序,小端字节序。 关于字节序的理解,请参考文章: 【应用开发】关于字节序的理解 https://jn10010537.blog.csdn.net/article/details/131860480 所谓…

探秘ArrayList源码:Java动态数组的背后实现

探秘ArrayList源码&#xff1a;Java动态数组的背后实现 一、成员变量二、构造器1、默认构造器2、带初始容量参数构造器3、指定collection元素参数构造器 三、add()方法扩容机制四、场景分析1、对于ensureExplicitCapacity&#xff08;&#xff09;方法1.1 add 进第 1 个元素到 …

Inno Setup打包winform、wpf程序可判断VC++和.net环境

Inno Setup打包winform、wpf程序可判断VC和.net环境 1、下载Inno Setup2、新建打包文件、开始打包1、新建打包文件2、填写 应用名称、版本号、公司名称、公司官网3、选择安装路径 Custom是指定默认路径、Program Files folder是默认C盘根目录4、选择程序启动exe文件 以及Addfol…

【Python】基于Python和Qt的海康威视相机开发

文章目录 0 前期教程1 前言2 例程解析3 图像获取4 其他问题与解决办法5 使用到的python包 0 前期教程 【项目实践】海康威视工业相机SDK开发小白版入门教程&#xff08;VS2015OpenCV4.5.1&#xff09; 1 前言 此前写了一篇基于C开发海康威视相机的博客&#xff0c;貌似看的人…

防抖与节流

一、防抖&#xff08;Debounce&#xff09; 一种用于优化性能和减少不必要请求的技术。 防抖函数会延迟触发某个事件处理函数&#xff0c;并在一段时间内只执行一次。如果在延迟时间内多次触发了同一个事件&#xff0c;防抖函数会取消之前的延迟执行&#xff0c;并重新开始计…

springboot实现qq邮箱发送邮件或者验证码

首先我先去qq邮箱或者网易邮箱开通POP3/IMAP/SMTP/Exchange/CardDAV 服务 它在左上角的设置——账户——往下滑就可以找到——然后点击开通 开通后就会得到一串授权码。如下图 接下来直接编写代码 首先我没导入依赖 <!-- 这个是邮箱验证--> <dependency> <group…

Python 模块 ddt 数据驱动测试

简介 ddt 提供了一种方便的方法来实现数据驱动测试&#xff08;Data-Driven Testing&#xff09;。数据驱动测试是一种测试方法&#xff0c;通过将测试数据与测试逻辑分开&#xff0c;可以使用不同的数据集来运行相同的测试用例。这样可以提高测试的灵活性和可维护性&#xff0…

【Deviation】50 Matplotlib Visualizations, Python实现,源码可复现

详情请参考博客: Top 50 matplotlib Visualizations 因编译更新问题&#xff0c;本文将稍作更改&#xff0c;以便能够顺利运行。 本文介绍一下5中图示&#xff1a; Diverging Bars Diverging Texts Diverging Dot Plot Diverging Lollipop Chart with Markers Area Chart 1 Di…

【Spring Cloud】git 仓库新的配置是如何刷新到各个微服务的原理步骤

文章目录 1. 第一次启动时2. 后续直接在 git 修改配置时3. 参考资料 本文描述了在 git 仓库修改了配置之后&#xff0c;新的配置是如何刷新到各个微服务的步骤 前言&#xff1a; 1、假设现有有 3 个微服务&#xff0c;1 个是 配置中心&#xff0c;另外 2 个是普通微服务&#x…

【C++】通过栈和队列学会使用适配器和优先队列学会仿函数的使用

&#x1f307;个人主页&#xff1a;平凡的小苏 &#x1f4da;学习格言&#xff1a;命运给你一个低的起点&#xff0c;是想看你精彩的翻盘&#xff0c;而不是让你自甘堕落&#xff0c;脚下的路虽然难走&#xff0c;但我还能走&#xff0c;比起向阳而生&#xff0c;我更想尝试逆风…

MAL文档

MAL文档 语法读取器宏特殊形式 内置符号内置函数算数运算谓词字符串解释器读取求值打印 原子序列操作字典元数据时间异常FFI 标准库符号函数宏 语法 空白符 所有的空白符会被忽略, 逗号也会被忽略; 以分号起始的内容直到行尾都被视为注释符号 符号中不允许含有空白符及[]{}()&…

pytorch安装GPU版本 (Cuda12.1)教程: Windows、Mac和Linux系统下GPU版PyTorch(CUDA 12.1)快速安装

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…

vscode设置java -Xmx最大堆内存

如果在vscode中直接运行java程序&#xff0c;想要改下每次运行的最大堆内存&#xff0c;按照如下修改 一、vscode安装java插件 当然前提是vscode在应用管理中已经安装了java语言的插件&#xff0c;Debugger for Java,如下图所示 二、CommandShiftP打开配置搜索框 三、搜索…

前端实现输入框实时搜索,【vue+el-input】

一般搜索都是调后端的接口&#xff0c;绑searchValue字段&#xff08;也有可能叫其他的字段名&#xff09;&#xff0c;通过后端的接口进行实时搜索 如果由前端自己实现搜索过滤的话也简单 1、input事件 <el-inputv-model"queryParams.searchValue"input"k…