当前位置: 首页 > news >正文

【linux学习篇】多线程之间资源共享---共享内存方式

文章目录

  • 1 概要
  • 2 linux下进程之间通信方式
  • 3 资源共享方式实现IPC通信
    • 3.1 server端共享内存/信号量/消息队列的使用
    • 3.2 client端共享内存/信号量/消息队列的使用
  • 4 资源下载

1 概要

本章节将会讲述如何通过资源共享的方式实现进程之间的资源交换,此众方式适用于大量数据交换,防止因拷贝而导致消耗过多时间

2 linux下进程之间通信方式

linux下不同线程之间的资源共享有多种方式,以下的这一篇博客写得很详细,博主就不重复讲述了:
进程间通信(IPC)介绍

3 资源共享方式实现IPC通信

我们通过 共享内存+信号量+消息队列的方式实现线程之间的通信。

其中信号量的意义是为了防止同一时间段有多个线程访问资源,从而导致资源被篡改等问题。消息队列的意义是server进程(发送资源的进程)可以通过消息队列分发指令给client进程(读取资源的进程),从而控制数据的的读取。

总的使用方法流程如下:
首先server进程运行,开辟共享内存区域,之后client进程链接到此开辟的内存,从而能够实现资源的访问。

关于共享内存,信号量,消息队列的使用注意事项如下小节所示。

3.1 server端共享内存/信号量/消息队列的使用

对于server进程创建共享内存池:
首先创建共享内存池代码如下:

    // 创建共享内存if ((shmid = shmget(key, sizeof(ImageData), shmPerm | IPC_CREAT)) == -1){perror("shmget error");return false;}// 连接共享内存shm = (ImageData *)shmat(shmid, 0, 0);if ((long)shm == -1){perror("shmat error");return false;}

其中
(1) key:唯一标识符(键值),通常由ftok()生成,用于标识系统范围内的共享内存段。
(2) ​​sizeof(ImageData)​​:共享内存段的大小,这里设置为存储ImageData结构所需的空间。
(3) ​​shmPerm | IPC_CREAT​​:标志位组合:
①​​ shmPerm​​:权限标志(如0644),控制用户/组/其他用户对共享内存的读写权限。
​​② IPC_CREAT​​:若共享内存不存在则创建它。

key生成如下:

    // 生成IPC keyif ((key = ftok(finalKeyPath, finalKeyId)) < 0){perror("ftok error");printf("Key path: %s, Key ID: %c\n", finalKeyPath, finalKeyId);return false;}

需要我们指定finalKeyPath和finalKeyId,经过这个key,在client线程中可以寻找到对应的内存池。

提醒一下,对于消息队列和信号量也依赖这个key值从而匹配对应的消息队列和信号量。如下:

    // 创建消息队列if ((msqid = msgget(key, msgPerm | IPC_CREAT)) == -1){perror("msgget error");shmdt(shm);return false;}// 创建信号量if ((semid = semget(key, 1, semPerm | IPC_CREAT)) == -1){perror("semget error");shmdt(shm);return false;}

对于往内存池中写数据时需要获取信号量,防止其他进程同一时间访问同一资源:

    // P操作:锁定共享内存if (sem_p() == -1){return false;}// 将cv::Mat数据写入共享内存shm->rows = image.rows;shm->cols = image.cols;shm->type = image.type();// image.elemSize()返回每个像素的字节大小shm->dataSize = image.total() * image.elemSize();if (shm->dataSize > sizeof(shm->data)){printf("Error: Image too large for shared memory! ""Size: %d, Max: %lu\n",shm->dataSize, sizeof(shm->data));sem_v();return false;}memcpy(shm->data, image.data, shm->dataSize);// V操作:解锁共享内存sem_v();

写完资源后通过消息队列通知client进程进行资源读取:

/***************************************************************************** 函数名: notifyClient()* 功 能: 通过消息队列向客户端发送通知消息* 输 入: msgType - 消息类型字符('r'表示读取图像,'q'表示退出)* 输 出: 返回true表示发送成功,false表示失败*/
bool ImageIPCServer::notifyClient(char msgType)
{if (!initialized){printf("Error: Server not initialized!\n");return false;}struct msg_form msg;msg.mtype = config->getInt("IPC", "message_type", 888);msg.mtext = msgType;if (msgsnd(msqid, &msg, sizeof(msg.mtext), 0) == -1){perror("msgsnd error");return false;}if (config->getBool("Logging", "verbose", true)){printf("Notification sent to client (type='%c')\n", msgType);}return true;
}

3.2 client端共享内存/信号量/消息队列的使用

client进程通过key链接到对应的内存区:

    // 获取已存在的共享内存if ((shmid = shmget(key, sizeof(ImageData), 0)) == -1){perror("shmget error - Make sure server is running first!");return false;}// 连接共享内存shm = (ImageData *)shmat(shmid, 0, 0);if ((long)shm == -1){perror("shmat error");return false;}

获取已创建的消息队列:

    // 获取已存在的消息队列if ((msqid = msgget(key, 0)) == -1){perror("msgget error");shmdt(shm);return false;}

获取已创建的信号量:

    // 获取已存在的信号量if ((semid = semget(key, 0, 0)) == -1){perror("semget error");shmdt(shm);return false;}

读取图片过程中也需要注意信号量的获取与释放:

/***************************************************************************** 函数名: readImage()* 功 能: 从共享内存读取图像数据到cv::Mat对象* 输 入: image - 用于存储读取图像的cv::Mat对象(引用传递)* 输 出: 返回true表示读取成功,false表示失败*/
bool ImageIPCClient::readImage(cv::Mat &image)
{if (!initialized){printf("Error: Client not initialized!\n");return false;}// P操作:锁定共享内存if (sem_p() == -1){return false;}// 从共享内存读取图像数据if (shm->dataSize <= 0 || shm->rows <= 0 || shm->cols <= 0){printf("Error: Invalid image data in shared memory\n");sem_v();return false;}// 创建cv::Mat并复制数据image = cv::Mat(shm->rows, shm->cols, shm->type);memcpy(image.data, shm->data, shm->dataSize);// V操作:解锁共享内存sem_v();printf("Image read from shared memory: %dx%d, type=%d, size=%d bytes\n",image.rows, image.cols, image.type(), shm->dataSize);return true;
}

因为代码较多,就不详细介绍了,介绍了大概的思路,细节读者可以慢慢看,整个目录结构如下:
在这里插入图片描述
其中ipc_client.cpp如下:

#include "ipc_client.h"
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>/***************************************************************************** 函数名: ImageIPCClient()* 功 能: 构造函数,初始化IPC客户端并加载配置文件* 输 入: configFile - 配置文件路径* 输 出: 无*/
ImageIPCClient::ImageIPCClient(const char *configFile) : key(0), shmid(-1), semid(-1), msqid(-1),shm(nullptr), initialized(false), running(false),imageCallback(nullptr), quitCallback(nullptr)
{config = &IPCConfig::getInstance();if (!config->loadFromFile(configFile)){printf("Warning: Failed to load config file, using defaults\n");}
}/***************************************************************************** 函数名: ~ImageIPCClient()* 功 能: 析构函数,断开IPC连接并清理资源* 输 入: 无* 输 出: 无*/
ImageIPCClient::~ImageIPCClient()
{disconnect();
}/***************************************************************************** 函数名: sem_p()* 功 能: P操作(等待/获取信号量),用于锁定共享资源* 输 入: 无(使用成员变量semid)* 输 出: 返回0表示成功,-1表示失败*/
int ImageIPCClient::sem_p()
{struct sembuf sbuf;sbuf.sem_num = 0;sbuf.sem_op = -1;sbuf.sem_flg = SEM_UNDO;if (semop(semid, &sbuf, 1) == -1){perror("P operation Error");return -1;}return 0;
}/***************************************************************************** 函数名: sem_v()* 功 能: V操作(释放/发送信号量),用于解锁共享资源* 输 入: 无(使用成员变量semid)* 输 出: 返回0表示成功,-1表示失败*/
int ImageIPCClient::sem_v()
{struct sembuf sbuf;sbuf.sem_num = 0;sbuf.sem_op = 1;sbuf.sem_flg = SEM_UNDO;if (semop(semid, &sbuf, 1) == -1){perror("V operation Error");return -1;}return 0;
}/***************************************************************************** 函数名: init()* 功 能: 初始化IPC客户端,连接服务器创建的共享内存、消息队列和信号量* 输 入: keyPath - IPC key文件路径(可选,优先使用配置文件)*         keyId - IPC key标识符(可选,优先使用配置文件)* 输 出: 返回true表示初始化成功,false表示失败*/
bool ImageIPCClient::init(const char *keyPath, int keyId)
{if (initialized){printf("Client already initialized!\n");return false;}// 从配置文件获取参数(与server完全相同)std::string configKeyPath = config->getString("IPC", "key_path", "/tmp/opencv_ipc_key");int configKeyId = config->getString("IPC", "key_id", "z")[0];const char *finalKeyPath = configKeyPath.c_str();int finalKeyId = configKeyId;// 检查key文件是否存在struct stat buffer;if (stat(finalKeyPath, &buffer) != 0){fprintf(stderr, "Key file does not exist: %s\n", finalKeyPath);fprintf(stderr, "Please start the server first!\n");return false;}// 生成IPC key(必须与server使用相同的参数)if ((key = ftok(finalKeyPath, finalKeyId)) < 0){perror("ftok error");printf("Key path: %s, Key ID: %c\n", finalKeyPath, finalKeyId);return false;}if (config->getBool("Logging", "verbose", true)){printf("Connecting using IPC key: 0x%x\n", key);}// 获取已存在的共享内存if ((shmid = shmget(key, sizeof(ImageData), 0)) == -1){perror("shmget error - Make sure server is running first!");return false;}// 连接共享内存shm = (ImageData *)shmat(shmid, 0, 0);if ((long)shm == -1){perror("shmat error");return false;}// 获取已存在的消息队列if ((msqid = msgget(key, 0)) == -1){perror("msgget error");shmdt(shm);return false;}// 获取已存在的信号量if ((semid = semget(key, 0, 0)) == -1){perror("semget error");shmdt(shm);return false;}initialized = true;printf("ImageIPCClient initialized successfully\n");return true;
}/***************************************************************************** 函数名: readImage()* 功 能: 从共享内存读取图像数据到cv::Mat对象* 输 入: image - 用于存储读取图像的cv::Mat对象(引用传递)* 输 出: 返回true表示读取成功,false表示失败*/
bool ImageIPCClient::readImage(cv::Mat &image)
{if (!initialized){printf("Error: Client not initialized!\n");return false;}// P操作:锁定共享内存if (sem_p() == -1){return false;}// 从共享内存读取图像数据if (shm->dataSize <= 0 || shm->rows <= 0 || shm->cols <= 0){printf("Error: Invalid image data in shared memory\n");sem_v();return false;}// 创建cv::Mat并复制数据image = cv::Mat(shm->rows, shm->cols, shm->type);memcpy(image.data, shm->data, shm->dataSize);// V操作:解锁共享内存sem_v();printf("Image read from shared memory: %dx%d, type=%d, size=%d bytes\n",image.rows, image.cols, image.type(), shm->dataSize);return true;
}/***************************************************************************** 函数名: waitForMessage()* 功 能: 阻塞等待消息队列中的消息* 输 入: msgType - 接收到的消息类型(引用传递)*        msgTypeFilter - 消息类型过滤器,默认888(从配置读取)* 输 出: 返回true表示成功接收消息,false表示失败*/
bool ImageIPCClient::waitForMessage(char &msgType, long msgTypeFilter)
{if (!initialized){printf("Error: Client not initialized!\n");return false;}// 如果使用默认值,从配置读取if (msgTypeFilter == 888){msgTypeFilter = config->getInt("IPC", "message_type", 888);}struct msg_form msg;if (msgrcv(msqid, &msg, sizeof(msg.mtext), msgTypeFilter, 0) == -1){perror("msgrcv error");return false;}msgType = msg.mtext;return true;
}/***************************************************************************** 函数名: processOneMessage()* 功 能: 处理一次消息,根据消息类型执行相应操作('r'读图像,'q'退出)* 输 入: 无* 输 出: 返回true表示继续处理,false表示收到退出信号应停止*/
bool ImageIPCClient::processOneMessage()
{if (!initialized){printf("Error: Client not initialized!\n");return false;}char msgType;if (!waitForMessage(msgType)){return false;}if (msgType == 'r'){printf("Received notification to read image\n");cv::Mat image;if (readImage(image)){// 如果设置了回调函数,调用它if (imageCallback){imageCallback(image);}else{// 默认行为:显示图像cv::imshow("Received Image", image);printf("Press any key on image window to continue...\n");cv::waitKey(0);}return true;}else{printf("Failed to read image\n");return false;}}else if (msgType == 'q'){printf("Received quit signal\n");// 如果设置了退出回调函数,调用它if (quitCallback){quitCallback();}return false; // 返回false表示应该退出}else{printf("Received unknown message type: '%c'\n", msgType);return true;}
}/***************************************************************************** 函数名: startListening()* 功 能: 启动消息监听循环(阻塞式),持续接收并处理消息直到收到退出信号* 输 入: 无* 输 出: 无*/
void ImageIPCClient::startListening()
{if (!initialized){printf("Error: Client not initialized!\n");return;}printf("Client started listening for messages...\n");running = true;while (running){if (!processOneMessage()){// processOneMessage返回false表示收到退出信号break;}}printf("Client stopped listening\n");
}/***************************************************************************** 函数名: stopListening()* 功 能: 停止消息监听循环* 输 入: 无* 输 出: 无*/
void ImageIPCClient::stopListening()
{running = false;
}/***************************************************************************** 函数名: setImageCallback()* 功 能: 设置图像接收回调函数,用于自定义图像处理逻辑* 输 入: callback - 图像处理回调函数指针* 输 出: 无*/
void ImageIPCClient::setImageCallback(ImageCallback callback)
{imageCallback = callback;
}/***************************************************************************** 函数名: setQuitCallback()* 功 能: 设置退出回调函数,用于自定义退出前的清理操作* 输 入: callback - 退出回调函数指针* 输 出: 无*/
void ImageIPCClient::setQuitCallback(QuitCallback callback)
{quitCallback = callback;
}/***************************************************************************** 函数名: disconnect()* 功 能: 断开IPC连接,分离共享内存并释放相关资源* 输 入: 无* 输 出: 无*/
void ImageIPCClient::disconnect()
{if (!initialized){return;}printf("Disconnecting client...\n");if (shm != nullptr){shmdt(shm);shm = nullptr;}cv::destroyAllWindows();initialized = false;running = false;printf("Client disconnected\n");
}/***************************************************************************** 函数名: isInitialized()* 功 能: 检查客户端是否已完成初始化* 输 入: 无* 输 出: 返回true表示已初始化,false表示未初始化*/
bool ImageIPCClient::isInitialized() const
{return initialized;
}/***************************************************************************** 函数名: isRunning()* 功 能: 检查客户端是否正在运行监听循环* 输 入: 无* 输 出: 返回true表示正在运行,false表示已停止*/
bool ImageIPCClient::isRunning() const
{return running;
}

ipc_client.h如下:

#ifndef IMAGE_IPC_CLIENT_H
#define IMAGE_IPC_CLIENT_H#include "../Common/IPCConfig.h"
#include <opencv2/opencv.hpp>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/msg.h>
#include <functional>// 共享内存中存储的图像信息结构(必须与Server一致)
struct ImageData
{int rows;                            // 图像行数int cols;                            // 图像列数int type;                            // 图像类型(CV_8UC3等)int dataSize;                        // 数据大小unsigned char data[1024 * 1024 * 3]; // 图像数据缓冲区
};// 消息队列结构
struct msg_form
{long mtype;char mtext;
};// 回调函数类型定义
typedef std::function<void(const cv::Mat &)> ImageCallback;
typedef std::function<void()> QuitCallback;class ImageIPCClient
{
private:key_t key;int shmid;int semid;int msqid;ImageData *shm;bool initialized;bool running;IPCConfig *config; // 添加配置指针// 回调函数ImageCallback imageCallback;QuitCallback quitCallback;// P操作(等待信号量)int sem_p();// V操作(释放信号量)int sem_v();public:// 构造函数ImageIPCClient(const char *configFile = "config.cfg");// 析构函数~ImageIPCClient();// 禁止拷贝ImageIPCClient(const ImageIPCClient &) = delete;ImageIPCClient &operator=(const ImageIPCClient &) = delete;// 初始化IPC资源bool init(const char *keyPath = ".", int keyId = 'z');// 从共享内存读取图像bool readImage(cv::Mat &image);// 等待并接收一条消息bool waitForMessage(char &msgType, long msgTypeFilter = 888);// 启动监听循环(阻塞式)void startListening();// 停止监听void stopListening();// 设置图像接收回调函数void setImageCallback(ImageCallback callback);// 设置退出回调函数void setQuitCallback(QuitCallback callback);// 手动处理一次消息(非阻塞式使用)bool processOneMessage();// 断开连接void disconnect();// 检查是否已初始化bool isInitialized() const;// 检查是否正在运行bool isRunning() const;
};#endif // IMAGE_IPC_CLIENT_H

Makefile如下:

# Makefile for compiling samples_server.cpp with OpenCV and IPC# 编译器
CXX = g++
CXXFLAGS = -Wall -std=c++11# 目标文件和输出目录
TARGET = client
OUT_DIR = out# 源文件
SRC = samples_client.cpp ipc_client.cpp ../Common/IPCConfig.cpp# 目标对象文件(放在out目录中,使用扁平结构)
OBJ = $(addprefix $(OUT_DIR)/, $(notdir $(SRC:.cpp=.o)))# 头文件目录
INCLUDES = -I. -I/usr/include/opencv -I../Common# OpenCV 库路径
OPENCV_LIBS = `pkg-config --libs opencv`# 系统库
SYS_LIBS = -lrt -pthread# 确保输出目录存在
$(shell mkdir -p $(OUT_DIR))# 生成目标
$(OUT_DIR)/$(TARGET): $(OBJ)$(CXX) $(CXXFLAGS) $(OBJ) $(INCLUDES) $(OPENCV_LIBS) $(SYS_LIBS) -o $@# 编译当前目录的 .cpp 文件
$(OUT_DIR)/%.o: %.cpp$(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@# 编译 ../Common 目录的 .cpp 文件
$(OUT_DIR)/%.o: ../Common/%.cpp$(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@# 清理生成的文件
clean:rm -rf $(OUT_DIR).PHONY: all clean

samples_client.cpp:

#include "ipc_client.h"
#include <iostream>int main() {std::cout << "========================================" << std::endl;std::cout << "   OpenCV IPC Image Client" << std::endl;std::cout << "========================================" << std::endl;// 创建客户端实例ImageIPCClient client("/home/book/Desktop/lbh/04_IPC/temp/config.cfg");// 初始化客户端if (!client.init()) {std::cerr << "\n✗ Failed to initialize client!" << std::endl;std::cerr << "  Make sure the server is running first!" << std::endl;return -1;}std::cout << "\n✓ Client initialized successfully!" << std::endl;std::cout << "  Waiting for images from server..." << std::endl;std::cout << "========================================" << std::endl;// 启动监听(阻塞式,会自动处理接收到的图像)client.startListening();std::cout << "\nClient terminated." << std::endl;return 0;
}

----------------------------------------ipc_server---------------------------------
ipc_server.c:

#include "ipc_server.h"
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>/***************************************************************************** 函数名: ImageIPCServer()* 功 能: 构造函数,初始化IPC服务器并加载配置文件* 输 入: configFile - 配置文件路径* 输 出: 无*/
ImageIPCServer::ImageIPCServer(const char *configFile): key(0), shmid(-1), semid(-1), msqid(-1),shm(nullptr), initialized(false)
{config = &IPCConfig::getInstance();if (!config->loadFromFile(configFile)){printf("Warning: Failed to load config file, using defaults\n");}if (config->getBool("Logging", "debug", false)){config->printAll();}
}/***************************************************************************** 函数名: ~ImageIPCServer()* 功 能: 析构函数,清理IPC资源(共享内存、信号量、消息队列)* 输 入: 无* 输 出: 无*/
ImageIPCServer::~ImageIPCServer()
{cleanup();
}/***************************************************************************** 函数名: sem_p()* 功 能: P操作(等待/获取信号量),用于锁定共享资源* 输 入: 无(使用成员变量semid)* 输 出: 返回0表示成功,-1表示失败*/
int ImageIPCServer::sem_p()
{struct sembuf sbuf;sbuf.sem_num = 0;        // 操作信号量集合中的第0个信号量sbuf.sem_op = -1;        // 操作类型:-1表示P操作(获取信号量)sbuf.sem_flg = SEM_UNDO; // 标志:如果进程异常终止,自动撤销此操作if (semop(semid, &sbuf, 1) == -1){perror("P operation Error");return -1;}return 0;
}/***************************************************************************** 函数名: sem_v()* 功 能: V操作(释放/发送信号量),用于解锁共享资源* 输 入: 无(使用成员变量semid)* 输 出: 返回0表示成功,-1表示失败*/
int ImageIPCServer::sem_v()
{struct sembuf sbuf;sbuf.sem_num = 0;sbuf.sem_op = 1;sbuf.sem_flg = SEM_UNDO;if (semop(semid, &sbuf, 1) == -1){perror("V operation Error");return -1;}return 0;
}/***************************************************************************** 函数名: init()* 功 能: 初始化IPC资源,创建共享内存、消息队列和信号量* 输 入: keyPath - IPC key文件路径(可选,优先使用配置文件)*         keyId - IPC key标识符(可选,优先使用配置文件)* 输 出: 返回true表示初始化成功,false表示失败*/
bool ImageIPCServer::init(const char *keyPath, int keyId)
{if (initialized){printf("Server already initialized!\n");return false;}// 从配置文件获取参数std::string configKeyPath = config->getString("IPC", "key_path", "/tmp/opencv_ipc_key");int configKeyId = config->getString("IPC", "key_id", "z")[0];bool autoCreate = config->getBool("IPC", "auto_create_key_file", true);// 使用配置文件的值const char *finalKeyPath = configKeyPath.c_str();int finalKeyId = configKeyId;// 如果需要,自动创建key文件if (autoCreate){struct stat buffer;if (stat(finalKeyPath, &buffer) != 0){// 文件不存在,创建它FILE *fp = fopen(finalKeyPath, "w");if (fp == nullptr){perror("Failed to create key file");printf("Please check the path: %s\n", finalKeyPath);return false;}fprintf(fp, "OpenCV IPC Key File\n");fclose(fp);printf("Created key file: %s\n", finalKeyPath);}}// 生成IPC keyif ((key = ftok(finalKeyPath, finalKeyId)) < 0){perror("ftok error");printf("Key path: %s, Key ID: %c\n", finalKeyPath, finalKeyId);return false;}if (config->getBool("Logging", "verbose", true)){printf("Using IPC key: 0x%x (path='%s', id='%c')\n",key, finalKeyPath, finalKeyId);}// 获取权限配置int shmPerm = config->getOctal("SharedMemory", "shm_permission", 0666);int semPerm = config->getOctal("Semaphore", "sem_permission", 0666);int msgPerm = config->getOctal("MessageQueue", "msg_permission", 0666);if (config->getBool("Logging", "verbose", true)){printf("Permissions - SHM:0%o SEM:0%o MSG:0%o\n", shmPerm, semPerm, msgPerm);}// 创建共享内存if ((shmid = shmget(key, sizeof(ImageData), shmPerm | IPC_CREAT)) == -1){perror("shmget error");return false;}// 连接共享内存shm = (ImageData *)shmat(shmid, 0, 0);if ((long)shm == -1){perror("shmat error");return false;}// 创建消息队列if ((msqid = msgget(key, msgPerm | IPC_CREAT)) == -1){perror("msgget error");shmdt(shm);return false;}// 创建信号量if ((semid = semget(key, 1, semPerm | IPC_CREAT)) == -1){perror("semget error");shmdt(shm);return false;}// 初始化信号量union semun sem_union;sem_union.val = config->getInt("Semaphore", "initial_value", 1);if (semctl(semid, 0, SETVAL, sem_union) == -1){perror("semctl error");shmdt(shm);return false;}initialized = true;printf("ImageIPCServer initialized successfully\n");return true;
}/***************************************************************************** 函数名: writeImage()* 功 能: 将cv::Mat图像数据写入共享内存* 输 入: image - 待写入的cv::Mat图像对象* 输 出: 返回true表示写入成功,false表示失败(图像为空或过大)*/
bool ImageIPCServer::writeImage(const cv::Mat &image)
{if (!initialized){printf("Error: Server not initialized!\n");return false;}if (image.empty()){printf("Error: Empty image!\n");return false;}printf("Writing image: %dx%d, type=%d\n",image.rows, image.cols, image.type());// P操作:锁定共享内存if (sem_p() == -1){return false;}// 将cv::Mat数据写入共享内存shm->rows = image.rows;shm->cols = image.cols;shm->type = image.type();// image.elemSize()返回每个像素的字节大小shm->dataSize = image.total() * image.elemSize();if (shm->dataSize > sizeof(shm->data)){printf("Error: Image too large for shared memory! ""Size: %d, Max: %lu\n",shm->dataSize, sizeof(shm->data));sem_v();return false;}memcpy(shm->data, image.data, shm->dataSize);// V操作:解锁共享内存sem_v();printf("Image written to shared memory (%d bytes)\n", shm->dataSize);return true;
}/***************************************************************************** 函数名: writeImageAndNotify()* 功 能: 将图像写入共享内存并通过消息队列通知客户端* 输 入: image - 待写入的cv::Mat图像对象*         msgType - 消息类型字符,默认'r'表示读取图像* 输 出: 返回true表示写入并通知成功,false表示失败*/
bool ImageIPCServer::writeImageAndNotify(const cv::Mat &image, char msgType)
{if (!writeImage(image)){return false;}return notifyClient(msgType);
}/***************************************************************************** 函数名: writeImageFromFile()* 功 能: 从文件加载图像并写入共享内存* 输 入: imagePath - 图像文件路径* 输 出: 返回true表示加载并写入成功,false表示失败*/
bool ImageIPCServer::writeImageFromFile(const char *imagePath)
{if (!initialized){printf("Error: Server not initialized!\n");return false;}cv::Mat image = cv::imread(imagePath);if (image.empty()){printf("Failed to load image: %s\n", imagePath);return false;}return writeImage(image);
}/***************************************************************************** 函数名: writeImageFromFileAndNotify()* 功 能: 从文件加载图像、写入共享内存并通知客户端* 输 入: imagePath - 图像文件路径*         msgType - 消息类型字符,默认'r'表示读取图像* 输 出: 返回true表示加载、写入并通知成功,false表示失败*/
bool ImageIPCServer::writeImageFromFileAndNotify(const char *imagePath, char msgType)
{if (!writeImageFromFile(imagePath)){return false;}return notifyClient(msgType);
}/***************************************************************************** 函数名: notifyClient()* 功 能: 通过消息队列向客户端发送通知消息* 输 入: msgType - 消息类型字符('r'表示读取图像,'q'表示退出)* 输 出: 返回true表示发送成功,false表示失败*/
bool ImageIPCServer::notifyClient(char msgType)
{if (!initialized){printf("Error: Server not initialized!\n");return false;}struct msg_form msg;msg.mtype = config->getInt("IPC", "message_type", 888);msg.mtext = msgType;if (msgsnd(msqid, &msg, sizeof(msg.mtext), 0) == -1){perror("msgsnd error");return false;}if (config->getBool("Logging", "verbose", true)){printf("Notification sent to client (type='%c')\n", msgType);}return true;
}/***************************************************************************** 函数名: sendQuitSignal()* 功 能: 向客户端发送退出信号,通知客户端终止运行* 输 入: 无* 输 出: 返回true表示发送成功,false表示失败*/
bool ImageIPCServer::sendQuitSignal()
{return notifyClient('q');
}/***************************************************************************** 函数名: getMaxImageSize()* 功 能: 获取共享内存可容纳的最大图像数据大小(字节数)* 输 入: 无* 输 出: 返回共享内存数据区的最大字节数*/
size_t ImageIPCServer::getMaxImageSize() const
{return sizeof(((ImageData *)0)->data);
}/***************************************************************************** 函数名: canFitImage()* 功 能: 检查指定图像是否可以放入共享内存* 输 入: image - 待检查的cv::Mat图像对象* 输 出: 返回true表示可以放入,false表示图像过大或为空*/
bool ImageIPCServer::canFitImage(const cv::Mat &image) const
{if (image.empty())return false;int dataSize = image.total() * image.elemSize();return dataSize <= sizeof(((ImageData *)0)->data);
}/***************************************************************************** 函数名: cleanup()* 功 能: 清理所有IPC资源,删除共享内存、信号量和消息队列* 输 入: 无* 输 出: 无*/
void ImageIPCServer::cleanup()
{if (!initialized){return;}printf("Cleaning up IPC resources...\n");if (shm != nullptr){shmdt(shm);shmctl(shmid, IPC_RMID, 0);}union semun sem_union;semctl(semid, 0, IPC_RMID, sem_union); // 删除信号量集msgctl(msqid, IPC_RMID, 0);initialized = false;shm = nullptr;printf("Cleanup complete\n");
}/***************************************************************************** 函数名: isInitialized()* 功 能: 检查服务器是否已完成初始化* 输 入: 无* 输 出: 返回true表示已初始化,false表示未初始化*/
bool ImageIPCServer::isInitialized() const
{return initialized;
}

ipc_server.h:

#ifndef IMAGE_IPC_SERVER_H
#define IMAGE_IPC_SERVER_H#include <opencv2/opencv.hpp>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/msg.h>
#include "../Common/IPCConfig.h"// 共享内存中存储的图像信息结构
struct ImageData
{int rows;                            // 图像行数int cols;                            // 图像列数int type;                            // 图像类型(CV_8UC3等)int dataSize;                        // 数据大小unsigned char data[1024 * 1024 * 3]; // 图像数据缓冲区(根据需要调整大小)
};// 消息队列结构
struct msg_form
{long mtype;char mtext;
};// 信号量联合体
union semun
{int val;struct semid_ds *buf;unsigned short *array;
};class ImageIPCServer
{
private:key_t key;int shmid;int semid;int msqid;ImageData *shm;bool initialized;IPCConfig *config; // 添加配置指针// P操作(等待信号量)int sem_p();// V操作(释放信号量)int sem_v();public:// 构造函数ImageIPCServer();// 析构函数~ImageIPCServer();// 构造函数 - 自动加载配置ImageIPCServer(const char *configFile = "config.cfg");// 初始化IPC资源bool init(const char *keyPath = ".", int keyId = 'z');// 写入图像到共享内存bool writeImage(const cv::Mat &image);// 写入图像并通知客户端bool writeImageAndNotify(const cv::Mat &image, char msgType = 'r');// 从文件加载图像并写入bool writeImageFromFile(const char *imagePath);// 从文件加载图像并通知客户端bool writeImageFromFileAndNotify(const char *imagePath, char msgType = 'r');// 发送消息通知客户端bool notifyClient(char msgType = 'r');// 发送退出信号bool sendQuitSignal();// 获取共享内存最大容量(字节)size_t getMaxImageSize() const;// 检查图像是否可以放入共享内存bool canFitImage(const cv::Mat &image) const;// 清理资源void cleanup();// 检查是否已初始化bool isInitialized() const;
};#endif // IMAGE_IPC_SERVER_H

Makefile:

# Makefile for compiling samples_server.cpp with OpenCV and IPC# 编译器
CXX = g++
CXXFLAGS = -Wall -std=c++11# 目标文件和输出目录
TARGET = server
OUT_DIR = out# 源文件
SRC = samples_server.cpp ipc_server.cpp ../Common/IPCConfig.cpp# 目标对象文件(放在out目录中,使用扁平结构)
OBJ = $(addprefix $(OUT_DIR)/, $(notdir $(SRC:.cpp=.o)))# 头文件目录
INCLUDES = -I. -I/usr/include/opencv -I../Common# OpenCV 库路径
OPENCV_LIBS = `pkg-config --libs opencv`# 系统库
SYS_LIBS = -lrt -pthread# 确保输出目录存在
$(shell mkdir -p $(OUT_DIR))# 生成目标
$(OUT_DIR)/$(TARGET): $(OBJ)$(CXX) $(CXXFLAGS) $(OBJ) $(INCLUDES) $(OPENCV_LIBS) $(SYS_LIBS) -o $@# 编译当前目录的 .cpp 文件
$(OUT_DIR)/%.o: %.cpp$(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@# 编译 ../Common 目录的 .cpp 文件
$(OUT_DIR)/%.o: ../Common/%.cpp$(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@# 清理生成的文件
clean:rm -rf $(OUT_DIR).PHONY: all clean

samples_server.cpp:

#include "ipc_server.h"
#include <iostream>
#include <string>int main() {std::cout << "========================================" << std::endl;std::cout << "   OpenCV IPC Image Server" << std::endl;std::cout << "========================================" << std::endl;// 创建服务器实例,加载配置文件ImageIPCServer server("/home/book/Desktop/lbh/04_IPC/temp/config.cfg");// 初始化服务器if (!server.init()) {std::cerr << "Failed to initialize server!" << std::endl;return -1;}std::cout << "\nServer started successfully!" << std::endl;std::cout << "image size configuration: " << server.getMaxImageSize() << " bytes" << std::endl;std::cout << "------------------------- 640x480 RGB: 921600" << std::endl;std::cout << "------------------------- 1920x1080 RGB: 6220800" << std::endl;std::cout << "------------------------- 1024x1024 RGB: 3145728" << std::endl;std::cout << "Max image size: " << server.getMaxImageSize() << " bytes" << std::endl;std::cout << "\nCommands:" << std::endl;std::cout << "  w <image_path> - Write image from file and notify client" << std::endl;std::cout << "  l <image_path> - Load image only (no notification)" << std::endl;std::cout << "  n              - Notify client to read image" << std::endl;std::cout << "  c              - Capture from camera" << std::endl;std::cout << "  q              - Quit and notify client" << std::endl;std::cout << "========================================" << std::endl;bool running = true;std::string input;while (running) {std::cout << "\nServer> ";std::cin >> input;if (input == "w") {// 从文件加载并发送图像std::string imagePath;std::cout << "Enter image path: ";std::cin >> imagePath;if (server.writeImageFromFileAndNotify(imagePath.c_str())) {std::cout << "✓ Image sent successfully!" << std::endl;} else {std::cout << "✗ Failed to send image!" << std::endl;}} else if (input == "l") {// 仅加载图像到共享内存std::string imagePath;std::cout << "Enter image path: ";std::cin >> imagePath;if (server.writeImageFromFile(imagePath.c_str())) {std::cout << "✓ Image loaded to shared memory" << std::endl;std::cout << "  (Use 'n' to notify client)" << std::endl;} else {std::cout << "✗ Failed to load image!" << std::endl;}} else if (input == "n") {// 通知客户端读取if (server.notifyClient('r')) {std::cout << "✓ Notification sent to client" << std::endl;} else {std::cout << "✗ Failed to send notification!" << std::endl;}} else if (input == "c") {// 从摄像头捕获std::cout << "Opening camera..." << std::endl;cv::VideoCapture cap(0);if (!cap.isOpened()) {std::cout << "✗ Failed to open camera!" << std::endl;continue;}std::cout << "Camera opened. Press 's' to send frame, 'q' to quit camera mode" << std::endl;cv::Mat frame;bool cameraRunning = true;while (cameraRunning) {cap >> frame;if (frame.empty()) {std::cout << "✗ Failed to capture frame!" << std::endl;break;}// 显示摄像头画面cv::imshow("Server Camera", frame);char key = cv::waitKey(30);if (key == 's' || key == 'S') {// 发送当前帧if (server.writeImageAndNotify(frame)) {std::cout << "✓ Frame sent!" << std::endl;} else {std::cout << "✗ Failed to send frame!" << std::endl;}} else if (key == 'q' || key == 'Q') {cameraRunning = false;}}cap.release();cv::destroyWindow("Server Camera");std::cout << "Camera closed" << std::endl;} else if (input == "q") {// 退出std::cout << "Sending quit signal to client..." << std::endl;server.sendQuitSignal();running = false;} else {std::cout << "Unknown command: " << input << std::endl;std::cout << "Type 'w', 'l', 'n', 'c', or 'q'" << std::endl;std::cin.clear();std::cin.ignore(10000, '\n');}}std::cout << "\nServer shutting down..." << std::endl;return 0;
}

---------------------------------------------config-------------------------------
config.cfg:

# IPC Configuration File
# 进程间通信配置文件
# 添加格式满足
# [section]
# key = value[IPC]
# IPC Key 生成参数
# ftok() 函数使用的路径,必须是一个存在的文件或目录
key_path = /tmp/opencv_ipc_key# ftok() 函数使用的项目ID (单个字符,建议使用 a-z 或 A-Z)
key_id = z# 消息队列类型过滤器
message_type = 888[SharedMemory]
# 共享内存中图像数据缓冲区的大小(字节)
# 默认: 3145728 (1024*1024*3 = 3MB,可存储1024x1024的RGB图像)
# 根据实际需要调整,例如:
#   - 640x480 RGB: 921600 字节
#   - 1920x1080 RGB: 6220800 字节
#   - 1024x1024 RGB: 3145728 字节
image_buffer_size = 1920x1080 # 共享内存权限 (八进制表示)
shm_permission = 0666[Semaphore]
# 信号量初始值
initial_value = 1# 信号量权限 (八进制表示)
sem_permission = 0666[MessageQueue]
# 消息队列权限 (八进制表示)
msg_permission = 0666[Display]
# 是否自动显示接收到的图像 (client)
auto_display = true# 图像显示窗口名称
window_name = IPC Image Viewer# 显示等待时间(毫秒),0表示一直等待
display_wait_time = 0[Logging]
# 是否启用详细日志
verbose = true# 是否显示调试信息
debug = false[Performance]
# 图像质量压缩(仅当图像过大时)
# 0 = 不压缩, 1-100 = JPEG质量
auto_compress_quality = 85# 自动调整图像大小的最大尺寸(像素)
# 如果图像宽度或高度超过此值,将自动缩放
max_image_dimension = 1920

IPCCommon.h:

#ifndef IPC_COMMON_H
#define IPC_COMMON_H#include "IPCConfig.h"// 从配置文件获取共享内存大小
inline size_t getImageBufferSize()
{IPCConfig &config = IPCConfig::getInstance();return config.getInt("SharedMemory", "image_buffer_size", 3145728); // 1024*1024*3 = 3MB
}// 共享内存中存储的图像信息结构
struct ImageData
{int rows;int cols;int type;int dataSize;// 使用动态大小需要特殊处理,这里保持固定大小// 实际使用时应该小于等于配置的大小unsigned char data[3145728]; // 默认3MB
};// 消息队列结构
struct msg_form
{long mtype;char mtext;
};#endif // IPC_COMMON_H

IPCConfig.cpp:

#include "IPCConfig.h"
#include <fstream>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <cctype>// 构造函数
IPCConfig::IPCConfig() : loaded(false) {}/***************************************************************************** 函数名: trim()* 功 能: 去除字符串首尾的空白字符(空格、制表符、回车、换行)* 输 入: str - 待处理的字符串* 输 出: 返回去除首尾空白后的字符串,若全为空白则返回空串*/
std::string IPCConfig::trim(const std::string &str)
{size_t first = str.find_first_not_of(" \t\r\n");if (first == std::string::npos){return "";}size_t last = str.find_last_not_of(" \t\r\n");return str.substr(first, last - first + 1);
}/***************************************************************************** 函数名: parseLine()* 功 能: 解析配置文件的单行内容,识别section、key-value对* 输 入: line - 待解析的行内容*         section - 当前所在的section名称(引用传递)*         key - 解析出的键名(引用传递)*         value - 解析出的值(引用传递)* 输 出: 返回true表示成功解析出key-value对,false表示空行/注释/section行*/
bool IPCConfig::parseLine(const std::string &line, std::string &section,std::string &key, std::string &value)
{std::string trimmedLine = trim(line);// 跳过空行和注释if (trimmedLine.empty() || trimmedLine[0] == '#'){return false;}// 解析section [SectionName]if (trimmedLine[0] == '[' && trimmedLine[trimmedLine.length() - 1] == ']'){section = trim(trimmedLine.substr(1, trimmedLine.length() - 2));return false;}// 解析 key = valuesize_t pos = trimmedLine.find('=');if (pos != std::string::npos){key = trim(trimmedLine.substr(0, pos));value = trim(trimmedLine.substr(pos + 1));// 移除值后面的注释size_t commentPos = value.find('#');if (commentPos != std::string::npos){value = trim(value.substr(0, commentPos));}return true;}return false;
}/***************************************************************************** 函数名: loadFromFile()* 功 能: 从指定文件加载配置数据到内存* 输 入: filename - 配置文件路径* 输 出: 返回true表示加载成功,false表示文件打开失败*/
bool IPCConfig::loadFromFile(const char *filename)
{std::ifstream file(filename);if (!file.is_open()){std::cerr << "Failed to open config file: " << filename << std::endl;return false;}configData.clear();std::string line, currentSection;std::string key, value;while (std::getline(file, line)){if (parseLine(line, currentSection, key, value)){if (!currentSection.empty() && !key.empty()){std::string fullKey = currentSection + "." + key;configData[fullKey] = value;}}}file.close();loaded = true;std::cout << "Configuration loaded successfully from " << filename << std::endl;return true;
}/***************************************************************************** 函数名: getString()* 功 能: 获取指定section和key对应的字符串值* 输 入: section - 配置段名称*         key - 配置项键名*         defaultValue - 默认值(当键不存在时返回)* 输 出: 返回对应的配置值,若不存在则返回默认值*/
std::string IPCConfig::getString(const std::string &section,const std::string &key,const std::string &defaultValue) const
{std::string fullKey = section + "." + key;auto it = configData.find(fullKey);if (it != configData.end()){return it->second;}return defaultValue;
}/***************************************************************************** 函数名: getInt()* 功 能: 获取指定section和key对应的整数值* 输 入: section - 配置段名称*         key - 配置项键名*         defaultValue - 默认值(当键不存在或转换失败时返回)* 输 出: 返回对应的整数值,转换失败则返回默认值*/
int IPCConfig::getInt(const std::string &section,const std::string &key,int defaultValue) const
{std::string value = getString(section, key, "");if (value.empty()){return defaultValue;}try{return std::stoi(value);}catch (...){return defaultValue;}
}/***************************************************************************** 函数名: getBool()* 功 能: 获取指定section和key对应的布尔值* 输 入: section - 配置段名称*         key - 配置项键名*         defaultValue - 默认值(当键不存在时返回)* 输 出: 返回布尔值,识别true/yes/1/on为true,其他为false*/
bool IPCConfig::getBool(const std::string &section,const std::string &key,bool defaultValue) const
{std::string value = getString(section, key, "");if (value.empty()){return defaultValue;}// 转换为小写std::string lowerValue = value;std::transform(lowerValue.begin(), lowerValue.end(),lowerValue.begin(), ::tolower);return (lowerValue == "true" || lowerValue == "yes" ||lowerValue == "1" || lowerValue == "on");
}/***************************************************************************** 函数名: getDouble()* 功 能: 获取指定section和key对应的浮点数值* 输 入: section - 配置段名称*         key - 配置项键名*         defaultValue - 默认值(当键不存在或转换失败时返回)* 输 出: 返回对应的浮点数值,转换失败则返回默认值*/
double IPCConfig::getDouble(const std::string &section,const std::string &key,double defaultValue) const
{std::string value = getString(section, key, "");if (value.empty()){return defaultValue;}try{return std::stod(value);}catch (...){return defaultValue;}
}/***************************************************************************** 函数名: getOctal()* 功 能: 获取指定section和key对应的八进制整数值* 输 入: section - 配置段名称*         key - 配置项键名*         defaultValue - 默认值(当键不存在或转换失败时返回)* 输 出: 返回按八进制解析的整数值,转换失败则返回默认值*/
int IPCConfig::getOctal(const std::string &section,const std::string &key,int defaultValue) const
{std::string value = getString(section, key, "");if (value.empty()){return defaultValue;}try{return std::stoi(value, nullptr, 8);}catch (...){return defaultValue;}
}/***************************************************************************** 函数名: isLoaded()* 功 能: 检查配置文件是否已成功加载* 输 入: 无* 输 出: 返回true表示已加载,false表示未加载*/
bool IPCConfig::isLoaded() const
{return loaded;
}/***************************************************************************** 函数名: printAll()* 功 能: 打印所有已加载的配置项到标准输出(调试用)* 输 入: 无* 输 出: 无*/
void IPCConfig::printAll() const
{std::cout << "=== Configuration ===" << std::endl;for (const auto &pair : configData){std::cout << pair.first << " = " << pair.second << std::endl;}std::cout << "=====================" << std::endl;
}/***************************************************************************** 函数名: getInstance()* 功 能: 获取IPCConfig类的单例实例* 输 入: 无* 输 出: 返回IPCConfig的静态单例对象引用*/
IPCConfig &IPCConfig::getInstance()
{static IPCConfig instance; // 此处为staticreturn instance;
}

IPCConfig.h:

#ifndef IPC_CONFIG_H
#define IPC_CONFIG_H#include <string>
#include <map>class IPCConfig
{
private:std::map<std::string, std::string> configData;bool loaded;// 去除字符串首尾空白std::string trim(const std::string &str);// 解析配置行bool parseLine(const std::string &line, std::string &section,std::string &key, std::string &value);public:// 构造函数IPCConfig();// 加载配置文件bool loadFromFile(const char *filename);// 获取字符串值std::string getString(const std::string &section, const std::string &key,const std::string &defaultValue = "") const;// 获取整数值int getInt(const std::string &section, const std::string &key,int defaultValue = 0) const;// 获取布尔值bool getBool(const std::string &section, const std::string &key,bool defaultValue = false) const;// 获取浮点数值double getDouble(const std::string &section, const std::string &key,double defaultValue = 0.0) const;// 获取八进制整数值(用于权限)int getOctal(const std::string &section, const std::string &key,int defaultValue = 0666) const;// 检查配置是否已加载bool isLoaded() const;// 打印所有配置(调试用)void printAll() const;// 获取单例实例static IPCConfig &getInstance();
};#endif // IPC_CONFIG_H

4 资源下载

链接如下:
https://download.csdn.net/download/qq_54050349/92167378

http://www.dtcms.com/a/504034.html

相关文章:

  • 除了有效市场假说,还有哪些理论能更好的解释经济或者金融活动
  • 咨询手机网站建设平台400电话网站源码
  • 【阿里巴巴大数据之路】事实表设计
  • 建设网站之前都需要准备什么问题广东vs北京首钢
  • 四川住房城乡建设厅网站企业模拟网站建设
  • 多媒体标签全解析:img、audio、video标签详解
  • 【139套财务会计excel模板】免费分享
  • 免费网站如何被百度收录wordpress图片特效
  • 网站框架有哪些网站页面优化怎么做
  • 虚幻引擎虚拟制片入门教程 之 Sequencer 常用技巧
  • 个人网站备案简介怎么写企业网站建设的征求意见
  • 如何做网络营销推广工作做外贸seo优化的上市公司
  • 怎样建网站买东西电商网站管理系统模板下载
  • ping一下新浪网站怎么做树莓派wordpress博客
  • 网站建设服务器租用找网站的方法
  • 解码Linux文件IO之LCD屏原理及应用
  • Java集合体系 —— Map篇
  • 大连网站制作代理价格北京小程序开发价格
  • 内蒙古建设安全监督网站东道 网站建设
  • 跳格子游戏(1)
  • 谈谈你对Mysql 锁的理解
  • 网站开发有哪些模块想做分销商有什么平台
  • P14245 [CCPC 2024 Shandong I] 左移题解
  • 网站做支付宝花呗分期有哪些做家教网站
  • Altium Designer(AD24)Tools工具功能总结
  • 做分析图的地图网站自己做网站网页剧中
  • 产妇入院出院过程分享
  • Windows 安装 WSL2 全指南(2025 版)
  • StarsNote 1.0.9
  • 通信原理(007)——FFT脚本(超级实用简单)