基于boost的共享内存通信demo

文章目录

  • 前言
  • 一、共享内存管理
  • 二、图像算法服务中的IPC通信流程
  • 三、demo实验结果
  • 总结


前言

在一个系统比较复杂的时候,将模块独立成单独的进程有助于错误定位以及异常重启恢复,不至于某个模块发生崩溃导致整个系统崩溃。当通信数据量比较大时,例如图像数据,可以使用共享内存在进程间交互,比socket快很多。下面介绍一个利用Boost.interprocess和Boost.process模块进行进程间图像数据交互,以及子进程调用、卡死、崩溃检测的demo。代码链接在文章结尾。


一、共享内存管理

共享内存依靠字符串名称来对应,A进程创建的变量或者内存空间,在B进程都是依靠唯一的命名来查找的,下面看一段代码:

//进程A
//创建共享内存空间
bip::managed_shared_memory shm(bi::create_only, shm_name.c_str(), size);
// 创建分配器
SharedImage::ShmemAllocator allocator(shm.get_segment_manager());
// 在共享内存中构造 Image 对象s
hared_image = shm.construct<SharedImage>("Image")(allocator);quit = shm.construct<bool>("quit")();//进程B
//打开内存空间 
shm = bip::managed_shared_memory(bip::open_only, shm_name); 
//查找变量 
quit = shm.find<bool>("quit").first; image = shm.find<SharedImage>("Image").first; 

共享内存变量可以是任意类型,但是你要能保证它的内存都在申请的共享内存之上,就拿我们自己定义的SharedImage类型来举例:

// 使用共享内存分配器定义的 vector 来存储图像数据
typedef bip::allocator<unsigned char, bip::managed_shared_memory::segment_manager> ShmemAllocator;
typedef bip::vector<unsigned char, ShmemAllocator> ShmemVector;
struct SharedImage
{    int imgWidth;int imgHeight;int imgChannels;int imgStep;bool used;ShmemVector data;// 构造函数SharedImage(const ShmemAllocator& allocator) : data(allocator) {}
};

存储图像数据的data字段我们用了ShmemVector,必须给它分配一个内存分配器才可以使用。必须保证所有的内存都在共享内存之上。如果我们直接创建一个带指针的类型是不行的,例如cv::Mat,它指针所指向的图像数据不在共享内存之上。当然Mat也提供了Matallocator接口,理论上通过在 共享内存之上重写这个分配接口Mat是可以直接创建的,但奈何太复杂了,还是不建议这么做,容易出错。

要注意的是共享内存的销毁,内存不需要使用或者变量不用时:

bi::shared_memory_object::remove(shm_name);
shm.destroy<bool>("quit");

当程序异常结束时共享内存时不会自动释放的,我们重新打开时需要判断内存空间是否存在,然后销毁后重新创建:

static bip::managed_shared_memory create_shared_memory(std::string shm_name, size_t size) {try {// Check if the shared memory already existsbool shm_exists = false;try {bi::shared_memory_object shm(bi::open_only, shm_name.c_str(), bi::read_write);shm_exists = true;} catch (const bi::interprocess_exception& e) {// Shared memory does not existshm_exists = false;}// If shared memory exists, remove itif (shm_exists) {std::cout << "Shared memory already exists. Removing it..." << std::endl;bi::shared_memory_object::remove(shm_name.c_str());}// Create new shared memory        bip::managed_shared_memory shm(bi::create_only, shm_name.c_str(), size);std::cout << "Shared memory created successfully." << std::endl;return shm;} catch (const std::exception& e) {std::cerr << "Exception: " << e.what() << std::endl;}return bip::managed_shared_memory();
}

二、图像算法服务中的IPC通信流程

本文讨论的IPC通信场景是用来替代算法库API的,即将真正的算法代码跑在一个进程里面,API作为一个中间件跑在调用者的进程中,将输入写到共享内存,等待算法进程处理完毕后返回结果转给调用方。大致流程如下图所示,在两个进程中都有一个心跳保持线程来监控对方是否还存活,数据输入处理是串行的过程,通过自旋等待监控标记位变化来判断是有新数据需要处理,以及是否处理完毕。这边我没有用进程锁(进程间如果用锁以及条件变量同步有很坑的地方,只要一个进程崩溃,另一个就可能卡死!!!),因为是串行处理,不可能同时写一个标记位。

ipc通信流程
父进程部分逻辑:

void run(int argc, char* argv[]){try{std::vector<std::string> image_files;cv::glob(argv[1], image_files);// Read the images from the file systemfor (auto &file : image_files){cv::Mat img = cv::imread(file);if (img.empty())continue;std::cout << "read image from file: " << file << std::endl;while(!child.running()){//std::cout<<"wait for child process run"<<std::endl;std::this_thread::yield();}process(img);}}catch (const std::exception &e){std::cerr << "Exception: " << e.what() << std::endl;}}void process(cv::Mat img){TickCount tic;std::cout << "send data to child process" << std::endl;shared_image->imgWidth = img.cols;shared_image->imgHeight = img.rows;shared_image->imgChannels = img.channels();shared_image->imgStep = img.step;shared_image->data.assign(img.data, img.data + img.total() * img.elemSize());shared_image->used = false;std::cout << "Waiting for child process" << std::endl;static int count = 0;static int timeout_count=0;std::cout << "wait" << std::endl;bool timeout_flag = !my_timed_wait(construct_timeout_seconds(2), [&](){ return shared_image->used || *quit; });if (!timeout_flag){count++; timeout_count=0;}else{timeout_count++;if(timeout_count > 2){std::cout<<"child process timeout too many times, may be in dead loop, restart child process"<<std::endl;stop_child_process();}}std::cout <<"time:"<<tic.elapsed()<<"ms"<<std::endl;std::cout << "child process done" << std::endl;std::cout << "count: " << count << std::endl;}void heartbeat(){while (!*quit){auto now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();if (now - *timestamp_child > 3000 && check_start_timestamp()){// 子进程崩溃if(last_start_timestamp > 0){printf("child process crash\n");}start_child_process();//wait for child process to start//std::this_thread::sleep_for(std::chrono::milliseconds(5000));}*timestamp_parent = now;std::this_thread::yield();}}void start_child_process(){if(!check_start_timestamp()) return;if(child.running()) child.terminate();std::cout<<"start child process"<<std::endl;child = boost::process::child("child.exe");   std::cout<<"child pid:"<<child.id()<<std::endl; std::cout<<"start child process end"<<std::endl;  last_start_timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();}void stop_child_process(){child.terminate();}bool check_start_timestamp(){auto now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();if (now - last_start_timestamp < 5000)return false;return true;}

子进程逻辑:

 void run(){cv::Mat img;while (*quit == false){//std::cout << "Waiting for task" << std::endl;auto timeout = construct_timeout_seconds(2);my_timed_wait(timeout, [&](){ return *quit == true || image->used == false; }); // Wait until the condition variable is notifiedif (*quit)break;if (image->used == true)continue;//std::cout << "get task" << std::endl;img = cv::Mat(image->imgHeight, image->imgWidth, CV_8UC3, image->data.data(), image->imgStep).clone();image->used = true;//printf("Image size: %d x %d\n", img.cols, img.rows);// cond->notify_all();cv::imshow("image", img);cv::waitKey(100);}}void heartbeat(){while (!*quit){auto now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();if (now - *timestamp_parent > 3000){// 父进程退出或崩溃,退出子进程printf("parent process crash, exit\n");exit(0);}*timestamp_child = now;std::this_thread::yield();}}

三、demo实验结果

成功检测到卡死、崩溃并重启,耗时非常短!
检测到卡死
检测到崩溃
耗时


总结

  1. 共享内存创建后不会随着进程结束而释放,需要手动释放。共享内存根据字符串命名作为唯一标识,所以注意命名。
  2. 变量的所有内存都需要在共享内存之上才可以在进程间传递,所以需要注意含有指针的结构,指针本身以及指针所指向内容都需要在共享内存上,图像可以利用bip::vector存放内容。
  3. 慎用进程锁和条件变量进行同步,一方面是有进程崩溃导致锁和条件变量卡死的BUG,另一方面是发生竞争时比较耗时。
    完整项目代码链接

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

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

相关文章

AI视频生成-一键创作动漫

一、前言 随着深度学习技术和计算能力的进步&#xff0c;AI生成视频&#xff08;AIGV&#xff09;已经从一个研究概念演变成了一种实用工具&#xff0c;其应用场景也在不断拓展。从自动合成新闻报道到虚拟人物的互动视频&#xff0c;从电影特效生成到游戏场景的实时渲染&#…

C++基础知识6 vector

vector 1.vector的介绍及使用1.1 vector的介绍1.2 vector的使用1.2.1 vector的定义1.2.2 vector iterator 的使用1.2.3 vector 常用的接口1.2.4 vector 空间增长问题1.2.4 vector 迭代器失效问题。&#xff08;重点&#xff09; 2.vector模拟实现 1.vector的介绍及使用 1.1 ve…

Dubbo精要

1、为什么需要 Dubbo&#xff1f; 分布式系统中的服务调用和协调问题&#xff1a;在分布式系统中&#xff0c;服务之间的相互依赖会导致复杂的通信和协调问题。Dubbo提供了高效的服务调用和自动注册、发现等功能&#xff0c;使得构建分布式应用程序更加容易。服务治理和服务调…

局域网设备自动发现常用方法

文章目录 需求实现方法ARP (Address Resolution Protocol)Ping ip的流程抓包如下代码实现 mDNS 对比测试Avahi 介绍Avahi 安装Avahi 使用测试代码 需求 局域网设备自动发现是软件开发中的一个常见且重要的需求&#xff0c;它简化了设备间的协作机制&#xff0c;降低了软件各模…

实验九 多线程的处理

实验目的及要求 目的&#xff1a;理解线程的概念&#xff0c;掌握Java的多线程机制&#xff0c;会用多线程编写Java程序。 要求&#xff1a; 理解线程的概念会用Thread类创建线程会使用Runnable接口创建多线程对两种实现多线程方式的方式进行对比掌握线程的同步 二、实验环境…

Pygame中Sprite类实现多帧动画3-3

4 使用自定义类MySprite 使用自定义类MySprite实现多帧动画的步骤是首先创建MySprite类的实例&#xff0c;之后使用相关函数对该实例进行操作。 4.1 创建MySprite类的实例 创建MySprite类的实例的代码如图12所示。 图12 创建MySprite类的实例的代码 其中&#xff0c;变量dr…

TortoiseGit无法安装解决方案

Win11安装TortoiseGit报错&#xff0c;错误码&#xff1a;2503&#xff0c;如下图&#xff1a; 开始-右键-Windows PowerShell&#xff08;管理员&#xff09;/终端 (管理员) 输入 msiexec /package 安装程序所在绝对路径&#xff0c; 例如 : msiexec /package D:\我的资料…

利用鸢尾花数据集介绍PCA算法

PCA&#xff1a; 主成分分析&#xff08;PCA, Principal Component Analysis&#xff09;是一种常用的数据降维技术&#xff0c;它可以将高维数据转换为较低维数据&#xff0c;同时尽可能保留数据的主要信息。PCA通过寻找数据的主要方向&#xff0c;即方差最大的方向&#xff0…

小小GCD、LCM拿下拿下

目录 最大公约数&#xff08;GCD&#xff09; 最大公约数&#xff08;GCD&#xff09;求解&#xff1a; 一、辗转相除法 二、三目运算符 三、位运算 最大公约数&#xff08;GCD&#xff09;模板&#xff1a; 最大公约数&#xff08;GCD&#xff09;例题&#xff1a; 最…

stm32之硬件SPI读写W25Q64存储器应用案例

系列文章目录 1. stm32之SPI通信协议 2. stm32之软件SPI读写W25Q64存储器应用案例 3. stm32之SPI通信外设 文章目录 系列文章目录前言一、电路接线图二、应用案例代码三、应用案例代码分析3.1 基本思路3.2 相关库函数介绍3.3 MySPI模块3.3.1 模块初始化3.3.2 SPI基本时序单元模…

丰巢“闯关”港交所上市

社区中随处可见的智能快递柜&#xff0c;即将捧出一个IPO。 近日&#xff0c;丰巢控股有限公司&#xff08;下称“丰巢控股”或“丰巢”&#xff09;正式向港交所递交了招股书&#xff0c;华泰国际担任其独家保荐人。这将是继顺丰控股、顺丰房托、嘉里物流、顺丰同城之后&…

微服务CI/CD实践(六)Jenkins Docker 自动化构建部署Java微服务

微服务CI/CD实践系列&#xff1a; 微服务CI/CD实践&#xff08;一&#xff09;环境准备及虚拟机创建 微服务CI/CD实践&#xff08;二&#xff09;服务器先决准备 微服务CI/CD实践&#xff08;三&#xff09;gitlab部署及nexus3部署 微服务CI/CD实践&#xff08;四&#xff09…

未来餐饮革命:加入我们的智能餐厅代理、自主开拓市场计划!

系统开发集成商&#xff1a;如果您正在开发智慧餐厅系统&#xff0c;忙于寻找各种消费终端接入、那么我们将可以为您提供整套智慧餐厅系统解决方案&#xff0c;从前厅消费到后厨的明厨亮灶的解决方案。 集团公司&#xff1a;想集团化控制子公司食堂运营&#xff0c;又想以最低…

【LeetCode每日一题】——LCR 168.丑数

文章目录 一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目注意】六【题目示例】七【题目提示】八【解题思路】九【时间频度】十【代码实现】十一【提交结果】 一【题目类别】 优先队列 二【题目难度】 中等 三【题目编号】 LCR 168.丑数 四【题目描述…

【大数据】Hadoop里的“MySQL”——Hive,干货满满

【大数据】Hadoop里的“MySQL”——Hive&#xff0c;干货满满 文章脉络 Hive架构 HQL 表类型 创建表语法 分区 数据导入导出 函数 内置函数 UDF Java Python 在阅读本文前&#xff0c;请确保已经对Hadoop的三大组件&#xff08;HDFS、MapReduce、YARN&#xff09;有…

分布式协调服务--ZooKeeper

文章目录 ZooKeeperzk的由来zk解决了什么问题 ZK工作原理ZK数据模型zk功能1.命名服务2.状态同步3.配置中心4.集群管理 zk部署单机启动zk验证zk zk集群集群角色选举过程1.节点角色状态2.选举ID3.具体过程4.心跳机制5.ZAB协议 ZooKeeper 选举示例1.第一轮投票&#xff1a;2.节点收…

Unity TextMeshPro 设置竖排

默认竖排是这样的 但是我们要的竖排效果并不是这样我们要是竖排连续的根据文本限制来进行换行 第一步我们先设置文本的旋转Z轴为90如下图 然后我们给文本加一个Tag <rotate270> 如下图 但是这个效果还是不是我们想要的效果我们可以使用TexeMeshPro提供的一个选项EnableR…

Python画笔案例-041 绘制正方形阶梯

1、绘制正方形阶梯 通过 python 的turtle 库绘制正方形阶梯&#xff0c;如下图&#xff1a; 2、实现代码 绘制正方形阶梯&#xff0c;以下为实现代码&#xff1a; """正方形阶梯.py """ import turtledef draw_square(length):for _ in range(6…

以太网--TCP/IP协议(一)

概述 以太网是局域网的一种&#xff0c;其他的比如还有令牌环、FDDI。和局域网对应的就是广域网&#xff0c;如Internet&#xff0c;城域网等。 从网络层次看&#xff0c;局域网协议主要偏重于低层&#xff08;业内一般把物理层、数据链路层归为低层&#xff09;。以太网协议…

Qt工程使用MQTT-C库与mqtt服务器数据通信

实现mqtt订阅与发布话题&#xff0c;与mqtt服务器进行数据通信 编译环境&#xff1a;Qt5.15.2 vs2019 需要mqttc库&#xff1a;mqttc.lib, mqttc.dll&#xff08;根据MQTT-C源码编译出来的库&#xff0c;参考cmake编译MQTT-C源码-CSDN博客&#xff09; 一、Qt pro文件编写 …