(最终版)linux下python和c++相互调⽤共享内存通信
本⽂主要⽤于python和c++相互通信,通过共享内存相互传递数据,图像,数组,结构体。
python优势在于开发快速⽅便,有很多扩展库可⽤,且深度学习很多都是python写的。
c++底层速度快,但是开发慢,尤其是很多SLAM和图像处理的只有c++版本。
为了调试开发⽅便,有时候需要嫁接两个⼯程,根据⾃⼰实际需要决定。
⼤概思路
1 c++编译动态库完成各种共享内存的实际操作。
2 python端调⽤c++动态库进⾏共享内存数据交互。
3 c++端调⽤c++动态库进⾏共享内存数据交互。
主要⽤的的是ctypes
资料
共享内存
在 Linux 实现内存共享的函数主要有 shmget、shmat、shmdt、shmctl 这么四个。
1、shmget 得到或者创建⼀个共享内存对象
int shmget(key_t key, size_t size, int shmflg)
其中 key_t key 的值为⼀个IPC键值,可以通过IPC_PRIVATE 建⽴⼀个新的键值为0的共享对象,但这并不能保证与IPC对象的对应关系,不同进程之间需要同⼀个且不会重复的IPC键值来完成通信,⼀般此参数使⽤ftok函数来进⾏创建IPC键值。
size_t size 是映射共享内存的⼤⼩,单位为字节 (Byte),但在创建时这⾥的最⼩分配单位是⼀页,⼤致为4KB,当你超过4KB但是⼩于8KB时会主动分配两页,也就是不⾜⼀页(4KB)的按照⼀页计算。
int shmflg 参数是需要注明的操作模式。
0:取键值对应的共享内存,若此键值相应的共享内存不存在就报错。
IPC_CREAT:存在与键值对应的共享内存则返回标识符,若不存在则创建共享内存返回标识符。
IPC_CREAT|IPC_EXCL:不存在相应键值的共享内存则创建此共享内存,若存在则报错
返回值:成功返回共享内存的标识符,失败返回-1。
2、shmat 获得共享内存的地址(与进程建⽴映射)
void *shmat(int shmid, const void *shmaddr, int shmflg)
int shmid 接收⼀个共享内存标识符参数,通常由 shmget 返回。
const void *shmaddr 共享内存地址映射到进程哪块内存地址,若设 NULL 则让内核决定。
int shmflg 选择操作模式,设 SHM_RDONLY 为只读共享内存,其他或0为可读写。
返回值:成功返回映射完成的内存地址,失败返回-1。
3、shmdt 与进程断开映射关系
int shmdt(const void *shmaddr)
4、shmctl 管理共享内存(释放共享内存)
int shmctl(int shmid, int cmd, struct shmid_ds *buf)
int shmid 接收⼀个共享内存标识符参数,通常由 shmget 返回。
int cmd 是需要注明的操作模式。
IPC_STAT:获取共享内存状态,将共享内存的 shmid_ds 信息拷贝到 shmid_ds *buf 中。
IPC_SET:更改共享内存状态,将 shmid_ds *buf 所指内容拷贝到共享内存 shmid_ds 中。
IPC_RMID:删除释放这块共享内存。
struct shmid_ds *buf 内存管理结构体,这个结构在 shm.h 中定义,⽹上能查到信息。
返回值:成功返回0,失败返回-1。
简单实例
#include <stdio.h>
#include <string.h>
/
/共享内存
#include <sys/shm.h>
//挂起进程
#include <unistd.h>
int main(void)
{
//分配内存标志
int segment_id;
//要映射的地址
char* text;
//共享内存结构
struct shmid_ds shmbuffer;
//创建共享内存
segment_id = shmget((key_t)1234567, 1, IPC_CREAT);
//映射共享地址
text = (char*)shmat(segment_id, NULL, 0);
while (1)
{
//映射共享地址
//text = (char*)shmat(segment_id, NULL, 0); //断开映射前,不能重复映射否则内存泄露
//拷⼊共享地址
memcpy(text, "A", 2);
/
/10000微妙,10毫秒
usleep(10000);
//输出当前共享内存区域地址
printf("%s\n", text);
}
//断开映射
shmdt(text);
//释放共享内存块
shmctl(segment_id, IPC_RMID, 0);
return 0;
}
问题注意
共享内存使⽤完,例如每次更新⼀张图像后.
1每次程序要映射同⼀个地址的时候,要断开先前的映射,不然在映射的时候会认为地址被占⽤,重新随机映射分配新地址,造成共享内存不停的再增⼤.
shmdt("名字");
2最后是⽤完要释放共享内存
shmctl(segment_id, IPC_RMID, 0);
3 使⽤ shmget -> shmat -> shmctl 这样⼀个流程就能描述⼀个内存从创建到销毁的过程。
(创建)->(映射)-> (断开映射)->(销毁)
测试环境
ubuntu18.05
联想笔记本
⼯程
1编译
cd bulid
删除清空
cmake ..
make
⽣成
c++ 发送测试端 send
c++ 接收测试端 client
c++ python和c++的交互动态库 libpython2share.so
同时python的测试样例
python的发送端 py_send.py
python的接收端 py_client.py
2测试样例
测试数据:共享内存
-1图像1920*1080
-2 gps数组(经度维度⾼度时间戳)
-3 gps结构体(标志位字符串说明经度维度⾼度时间戳)
2-1 c++ 发送,c++接收
2-2 c++发送,pytho接受
2-3 python发送,c++接受
2-3 python发送,python接收
(如果是单纯的pyton接受,python有⾃⼰的共享内存机制,更好⽤)
3其他说明
3-1 while不要出现空循环(什么也不做),容易导致⼀个进程占⽤cpu,导致其他进程⽆法获取cpu控制权,⽆法修改共享内存数据,造成假死状态。在while中加⼊sleep(0.001)操作。
虽然是多核CPU,从道理上来说不应出现这种问题,但是各种程序测试都是类似问题。
4代码结构
<
# cmake needs this line
cmake_minimum_required(VERSION 3.1)
# Define project name
project(opencv_example_project)
find_package(OpenCV REQUIRED)
message(STATUS "OpenCV library status:")
message(STATUS "    version: ${OpenCV_VERSION}")
message(STATUS "    libraries: ${OpenCV_LIBS}")
# Declare the executable target built from your sources
add_library(python2share SHARED ApiShare_dll.cpp)
#add_executable(send send_example.cpp ApiShare_dll.cpp)
#add_executable(client recive_example.cpp ApiShare.cpp)
add_executable(send send_example.cpp)
add_executable(client recive_example.cpp)
# Link your application with OpenCV libraries
target_link_libraries(send ${OpenCV_LIBS} )
target_link_libraries(client ${OpenCV_LIBS} )
target_link_libraries(python2share ${OpenCV_LIBS})
C++动态库
ApiShare_dll.cpp
使⽤前修改以下数据:
1 图像分辨率默认1920*1080
#define IMAGE_W 1920  //图像宽
#define IMAGE_H  1080  //图像⾼
#define IMAGE_SIZE  IMAGE_W*IMAGE_H*3 //图⽚像素总⼤⼩ 3通道彩⾊
2 共享内存标识,默认⼀个共享内存地址,未来如果开启多个地址同可修改这个
#define Shm_addrees 1203 //共享内存地址标识
3 共享内存数据结构,未来根据⾃⼰需要修改
//共享内存-图像
typedef struct ShareData
{
int  flag;
int rows;//图像⾼
int cols;//图像宽
char imgdata[IMAGE_SIZE];//图像数据⼀维数据,之前⽤了cv::Mat不⾏,因为⽆法在结构体⾥初始化⼤⼩
float Gps[4];//保存gps信息经纬⾼时间戳
}ShareData_;
4 共享内存图像格式默认是char,其中类中图像变量cvoutImg 是⽤来暂存⼀个mat类型图像,便于直接访问⽆需转化才能访问。根据不同的数据修改图像类型
cv::Mat cvoutImg = cv::Mat(IMAGE_H,IMAGE_W,CV_8UC3,cv::Scalar(255, 255, 255));//bufHight,bufWidthl 
例如 zed双⽬相机出来的图格式是CV_8UC4⽽不是⽂件中默认的CV_8UC3。所以就要改成
cv::Mat cvoutImg = cv::Mat(IMAGE_H,IMAGE_W,CV_8UC4,cv::Scalar(255, 255, 255));//bufHight,bufWidthl 
linux下的sleep函数ApiShare_dll.cpp 完整代码
#ifndef SHARE
#define SHARE
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/shm.h>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。