Linux多进程数据交换--共享内存

news2024/12/29 11:07:44

个人博客地址: https://cxx001.gitee.io

基础

在linux系统开发当中,时常需要在多个进程之间交换数据,在多个进程之间交换数据,有很多方法,但最高效的方法莫过于共享内存。

linux共享内存是通过tmpfs这个文件系统来实现的,tmpfs文件系的目录为/dev/shm,/dev/shm是驻留在内存 RAM 当中的,因此读写速度与读写内存速度一样,/dev/shm的容量默认尺寸为系统内存大小的一半大小,使用df -h命令可以看到。但实际上它并不会真正的占用这块内存,如果/dev/shm/下没有任何文件,它占用的内存实际上就是0字节,仅在使用shm_open文件时,/dev/shm才会真正占用内存。

在Linux系统使用共享内存,一般用到以下几个函数:

// 用于创建或者打开共享内存文件
int shm_open(const char *name, int oflag, mode_t mode);
// 将打开的文件映射到内存
void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
// 取消内存映射
int munmap(void *addr, size_t length);
// 删除/dev/shm目录的文件
int shm_unlink(const char *name);
// 重置文件大小
int ftruncate(int fd, off_t length);

示例

1. 共享内存相关操作封装

SimuShareMem.h

#ifndef __SIMU_SHARE_MEM_
#define __SIMU_SHARE_MEM_

enum {
    SIMU_MAX_SHM_BLOCK_QUEUE_LEN = 1024,  // 队列长度
    SIMU_MAX_SHM_BLOCK_BUFF_LEN = 2048    // 缓冲区数据长度
};

// 共享内存块
typedef struct TagSimuShareMemBlock
{
    int ReadDataPtr;            // 读下标
    int WriteDataptr;           // 写下标
    unsigned long nCoverCount;  // 写覆盖次数(好像不准确)
    unsigned long nRepeatCount; // 读重复次数(没用到)
    unsigned long nDataType[SIMU_MAX_SHM_BLOCK_QUEUE_LEN];   // 数据类型
    unsigned long nDataLen[SIMU_MAX_SHM_BLOCK_QUEUE_LEN];    // 数据长度
    char szData[SIMU_MAX_SHM_BLOCK_QUEUE_LEN][SIMU_MAX_SHM_BLOCK_BUFF_LEN]; // 数据区 
}SimuShareMemBlock_t;

// 共享全双工节点
typedef struct TagSimuShareMemNode 
{
    int nReadShmfd;    // 读共享内存文件句柄
    int nWriteShmfd;   // 写共享内存文件句柄
    SimuShareMemBlock_t* pReadShm;  // 读共享内存区块
    SimuShareMemBlock_t* pWriteShm; // 写共享内存区块
    char szReadShmName[128];  // 读共享内存块名称
    char szWriteShmName[128]; // 写共享内存块名称
}SimuShareMemNode_t;

// 共享内存数据
typedef struct TagSimuShareMemData
{
    int nDataType;
    unsigned long ulDataLen;
    char* pData;
    unsigned long ulTsl;
    unsigned long ulTs2;
}SimuShareMemData_t;

int CreateShareMemNode(SimuShareMemNode_t* pNode, const char* szWriteShmName, const char* szReadShmName);
int OpenShareMemNode(SimuShareMemNode_t* pNode, const char* szWriteShmName, const char* szReadShmName);
int CloseShareMemNode(SimuShareMemNode_t* pNode);  // 取消内存映射
int UnlinkShareMem(const char* szWriteShmName, const char* szReadShmName); // 删除/dev/shm目录的文件
int IsNewShareMemData(SimuShareMemNode_t* pNode);
int ReadShareMemData(SimuShareMemNode_t* pNode, SimuShareMemData_t* pShmData);
int WriteShareMemData(SimuShareMemNode_t* pNode, int nDataType, const char* pData, unsigned long ulDataLen);

#endif

SimuShareMem.cpp

//#ifdef LINUX_PLATFORM
#include "SimuShareMem.h"
#include <stdbool.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>

static SimuShareMemBlock_t* CreateShareMemBlock(int* pFd, const char* szBlockName);
static SimuShareMemBlock_t* OpenShareMemBlock(int* pFd, const char* szBlockName);

SimuShareMemBlock_t* CreateShareMemBlock(int* pFd, const char* szBlockName)
{
    SimuShareMemBlock_t* pBlock = NULL;
    int hFd = 0;

    if (pFd == NULL || szBlockName == NULL) {
        return NULL;
    }

    // 打开文件如果没有就创建, 存在则打开失败返回-1
    hFd = shm_open(szBlockName, O_CREAT|O_EXCL|O_RDWR, S_IRWXU|S_IRWXG);
    if (hFd == -1) {
        if (errno == EEXIST) {
            hFd = shm_open(szBlockName, O_RDWR, S_IRWXU);
            if (hFd == -1) {
                return NULL;
            }
        } else {
            return NULL;
        }
    }

    // 重置文件大小
    if (ftruncate(hFd, sizeof(SimuShareMemBlock_t)) == -1) {
        close(hFd);
        return NULL;
    }

    // 将打开的文件映射到内存
    pBlock = (SimuShareMemBlock_t*)mmap(NULL, sizeof(SimuShareMemBlock_t), PROT_READ|PROT_WRITE, MAP_SHARED, hFd, 0);
    if (pBlock == NULL) {
        close(hFd);
        return NULL;
    }
    *pFd = hFd;

    return pBlock; // 共享内存地址
}

SimuShareMemBlock_t* OpenShareMemBlock(int* pFd, const char* szBlockName)
{
    SimuShareMemBlock_t* pBlock = NULL;
    int hFd = shm_open(szBlockName, O_RDWR, S_IRWXU);
    if (hFd == -1) {
        return NULL;
    }

    pBlock = (SimuShareMemBlock_t*)mmap(NULL, sizeof(SimuShareMemBlock_t), PROT_READ|PROT_WRITE, MAP_SHARED, hFd, 0);
    if (pBlock == NULL) {
        close(hFd);
        return NULL;
    }
    *pFd = hFd;

    return pBlock;
}

int CreateShareMemNode(SimuShareMemNode_t* pNode, const char* szWriteName, const char* szReadName)
{
    bool bError = false;
    if (pNode == NULL || szWriteName == NULL || szReadName == NULL) {
        return -1;
    }
    strcpy(pNode->szWriteShmName, szWriteName);
    strcpy(pNode->szReadShmName, szReadName);
    pNode->nReadShmfd = 0;
    pNode->nWriteShmfd = 0;

    do {
        pNode->pReadShm = CreateShareMemBlock(&pNode->nReadShmfd, pNode->szReadShmName);
        if (pNode->pReadShm == NULL) {
            bError = true;
            break;
        }
        pNode->pReadShm->nCoverCount = 0;
        pNode->pReadShm->nRepeatCount = 0;
        pNode->pReadShm->ReadDataPtr = 0;
        pNode->pReadShm->WriteDataptr = 0;

        pNode->pWriteShm = CreateShareMemBlock(&pNode->nWriteShmfd, pNode->szWriteShmName);
        if (pNode->pWriteShm == NULL) {
            bError = true;
            break;
        }
        pNode->pWriteShm->nCoverCount = 0;
        pNode->pWriteShm->nRepeatCount = 0;
        pNode->pWriteShm->ReadDataPtr = 0;
        pNode->pWriteShm->WriteDataptr = 0;
    } while(0);

    if(bError) {
        CloseShareMemNode(pNode);
        return -1;
    }
    return 0;
}

int OpenShareMemNode(SimuShareMemNode_t* pNode, const char* szWriteName, const char* szReadName)
{
   bool bError = false;
    if (pNode == NULL || szWriteName == NULL || szReadName == NULL) {
        return -1;
    }
    strcpy(pNode->szWriteShmName, szWriteName);
    strcpy(pNode->szReadShmName, szReadName);
    pNode->nReadShmfd = 0;
    pNode->nWriteShmfd = 0;

    do {
        pNode->pReadShm = OpenShareMemBlock(&pNode->nReadShmfd, pNode->szReadShmName);
        if (pNode->pReadShm == NULL) {
            bError = true;
            break;
        }
        
        // 这里注释是因为写的测试程序起来就写数据了, 所以读的测试程序获取这块空间时不能重置了.
        // 正常程序这里不要注释,所有进程都要启动了,才能往共享内存里读写数据.
        /*
        pNode->pReadShm->nCoverCount = 0;
        pNode->pReadShm->nRepeatCount = 0;
        pNode->pReadShm->ReadDataPtr = 0;
        pNode->pReadShm->WriteDataptr = 0;
        */

        pNode->pWriteShm = OpenShareMemBlock(&pNode->nWriteShmfd, pNode->szWriteShmName);
        if (pNode->pWriteShm == NULL) {
            bError = true;
            break;
        }
        /*
        pNode->pWriteShm->nCoverCount = 0;
        pNode->pWriteShm->nRepeatCount = 0;
        pNode->pWriteShm->ReadDataPtr = 0;
        pNode->pWriteShm->WriteDataptr = 0;
        */

    } while(0);

    if(bError) {
        CloseShareMemNode(pNode);
        return -1;
    }
    return 0;
}

int CloseShareMemNode(SimuShareMemNode_t* pNode)
{
    if (pNode == NULL) {
        return -1;
    }

    if (pNode->pReadShm != NULL) {
        // 取消内存映射
        munmap(pNode->pReadShm, sizeof(SimuShareMemBlock_t));
        pNode->pReadShm = NULL;
        close(pNode->nReadShmfd);
    }

    if (pNode->pWriteShm != NULL) {
        // 取消内存映射
        munmap(pNode->pWriteShm, sizeof(SimuShareMemBlock_t));
        pNode->pWriteShm = NULL;
        close(pNode->nWriteShmfd);
    }

    return 0;
}

int UnlinkShareMem(const char* szWriteName, const char* szReadName)
{
    // 删除/dev/shm目录的文件
    shm_unlink(szWriteName);
    shm_unlink(szReadName);
    return 0;
}

int IsNewShareMemData(SimuShareMemNode_t* pNode)
{
    SimuShareMemBlock_t* pShm = pNode->pReadShm;
    if (pShm->ReadDataPtr % SIMU_MAX_SHM_BLOCK_QUEUE_LEN == pShm->WriteDataptr % SIMU_MAX_SHM_BLOCK_QUEUE_LEN) {
        return -1;
    }
    return 0;
}

int ReadShareMemData(SimuShareMemNode_t* pNode, SimuShareMemData_t* pShmData)
{
    SimuShareMemBlock_t* pShm = pNode->pReadShm;
    unsigned long nReadIdx = 0;

    if (pShm->ReadDataPtr % SIMU_MAX_SHM_BLOCK_QUEUE_LEN == pShm->WriteDataptr % SIMU_MAX_SHM_BLOCK_QUEUE_LEN) {
        return -1;
    }

    nReadIdx = pShm->ReadDataPtr % SIMU_MAX_SHM_BLOCK_QUEUE_LEN;
    pShmData->nDataType = pShm->nDataType[nReadIdx];
    pShmData->ulDataLen = pShm->nDataLen[nReadIdx];
    pShmData->pData = (char*)malloc(pShmData->ulDataLen);
    memcpy((void*)pShmData->pData, pShm->szData[nReadIdx], pShmData->ulDataLen);
    pShm->ReadDataPtr += 1;
    pShm->ReadDataPtr %= SIMU_MAX_SHM_BLOCK_QUEUE_LEN;
    pShmData->ulTsl = time(NULL);
    return 0;
}

int WriteShareMemData(SimuShareMemNode_t* pNode, int nDataType, const char* pData, unsigned long ulDataLen)
{
    SimuShareMemBlock_t* pShm = pNode->pWriteShm;
    unsigned long nWriteIdx = 0;

    if (pShm->ReadDataPtr == (pShm->WriteDataptr + 1) % SIMU_MAX_SHM_BLOCK_QUEUE_LEN) {
        pShm->nCoverCount++;  // 这里不知道啥意思
    }

    nWriteIdx = pShm->WriteDataptr % SIMU_MAX_SHM_BLOCK_QUEUE_LEN;
    memcpy(pShm->szData[nWriteIdx], pData, ulDataLen);
    pShm->nDataLen[nWriteIdx] = ulDataLen;
    pShm->nDataType[nWriteIdx] = nDataType;
    pShm->WriteDataptr++;
    pShm->WriteDataptr %= SIMU_MAX_SHM_BLOCK_QUEUE_LEN;

    return 0;
}

//#endif

2. 使用示例

共享内存写数据: writer.c

编译命令: g++ writer.c SimuShareMem.h SimuShareMem.cpp -o writer -lrt

注意最后的 -lrt链接库不能少,不然shm_open等相关函数不认识 !

#include <stdio.h>
#include "SimuShareMem.h"

int main(int argc,char * argv[])
{
    SimuShareMemNode_t pNode;
    const char* szWriteShmName = "shm_writer";
    const char* szReadShmName = "shm_reader";
    CreateShareMemNode(&pNode, szWriteShmName, szReadShmName);

    char writeData[] = "test share memory writer.";
    int result = WriteShareMemData(&pNode, 1, writeData, sizeof(writeData));
    printf("result:%d, data:%s, len:%ld", result, writeData, sizeof(writeData));

    getchar();

    CloseShareMemNode(&pNode);
    UnlinkShareMem(szWriteShmName, szReadShmName);

    return 0;
}

共享内存读数据:reader.c

编译命令: g++ reader.c SimuShareMem.h SimuShareMem.cpp -o reader -lrt

#include <stdio.h>
#include "SimuShareMem.h"

int main(int argc,char * argv[])
{
    SimuShareMemNode_t pNode;
    const char* szWriteShmName = "shm_writer";
    const char* szReadShmName = "shm_reader";
    OpenShareMemNode(&pNode, szReadShmName, szWriteShmName);  // 这里的读是对面的写

    SimuShareMemData_t srData;
    int result = ReadShareMemData(&pNode, &srData);
    printf("result: %d, data: %s, len: %ld", result, srData.pData, srData.ulDataLen);

    getchar();
    return 0;
}

3. 测试结果

  • 测试机器

在这里插入图片描述

  • 分别启动writer/reader程序读写结果

在这里插入图片描述
在这里插入图片描述

  • /dev/shm目录下新增对应两个共享内存文件

在这里插入图片描述

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

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

相关文章

【漏洞案例】记一次aws-key泄露导致火币交易所被攻击案例

全球最大的加密货币交易所之一火币悄然修复了一个数据泄露事件&#xff0c;该泄露事件可能导致该公司的云存储被访问。火币无意中共享了一组凭证&#xff0c;授予其所有 Amazon Web Services S3 存储桶写入权限。 该公司使用 S3 存储桶来托管其 CDN 和网站。任何人都可以使用这…

shell中sed命令常用用法总结

1、sed命令匹配文本内容进行替换 #-i 可以直接在原始文本中匹配替换&#xff0c;s/匹配内容/需要替换的内容/g &#xff0c;g是以一行为全局&#xff0c;有g说明每行中匹配到的都需要替换&#xff0c;没有g说明&#xff0c;只需要替换每行中的第一个匹配到的。 sed -i s/pool …

前端 鱼骨图 elenemt ui

展示图 代码 <template><div><divclass"fishbone":style"{ height: calc((${topChiderH} ${topChiderH}) 33px) }"><div class"top-box"><divclass"flex-items"ref"topChilderen":style"…

使用预训练CNN生成图像嵌入(image embeddings)

文章目录 什么是图像嵌入&#xff1f;来自 Kaggle 的狗品种图像数据集从狗品种图像数据集生成图像嵌入参考 什么是图像嵌入&#xff1f; 图像嵌入是图像的低维表示。换句话说&#xff0c;它是图像的密集向量表示&#xff0c;可用于分类等许多任务。 例如&#xff0c;这些深度…

(0020) H5-Vue-router+Element-ui 搭建非常简单的dashboard

参考学习&#xff1a; Vue Vue-router Element-ui 搭建一个非常简单的dashboard demo demo参考&#xff1a;https://github.com/wangduanduan/vue-el-dashboard 在线预览 效果图&#xff1a; 使用到的技术&#xff1a; Vue Vue-router Element-ui webpack Normalize.css v…

final关键字 抽象类

final关键字 可以修饰类、属性、方法和局部变量【参数】。 使用场景 1、当不希望 类被继承时&#xff0c;可以用final修饰。比如不希望子类重写父类时。 2、当不希望子类重写父类的某个方法。注意&#xff1a;此时不能重写但能继承父类此方法使用。 3、当不希望类的某个属…

windows下在注册表中添加右键pycharm打开目录

1、winregedit打开注册表&#xff0c;并在shell下创建项&#xff0c;修改如下图右侧内容 2、在PyCharm下创建command项&#xff0c;并修改其内容 3、重启电脑 4、显示

比ping还好用的排障命令

遇到网络故障的时候&#xff0c;你一般会最先使用哪条命令进行排障&#xff1f; 除了Ping&#xff0c;还有Traceroute、Show、Telnet又或是Clear、Debug等等。 今天安排的&#xff0c;是Traceroute排障命令详解&#xff0c;给你分享2个经典排障案例哈。 01 Traceroute原理和功…

Cisco ISR 4000 Series IOS XE Release Dublin-17.11.1a ED

Cisco ISR 4000 Series IOS XE Release Dublin-17.11.1a ED 思科 4000 系列集成服务路由器 请访问原文链接&#xff1a;https://sysin.org/blog/cisco-isr-4000/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.org 思科 4000 系列…

Matlab无人机算法开发套件上新,快速实现控制算法到无人机平台的移植

在现有的旋翼无人机市场中&#xff0c;绝大部分可二次开发的产品都只提供C、C等SDK&#xff0c;但在科研类无人机这一细分领域中&#xff0c;更多的开发者偏好或善于使用Matlab进行算法的开发和验证。为了助力从事控制算法理论研究的开发者&#xff0c;能够独立完成控制算法到无…

举例说明什么是循环神经网络

循环神经网络&#xff08;Recurrent Neural Network, RNN&#xff09;是一种处理时间序列数据和自然语言等具有顺序信息的数据的神经网络模型。与普通的前馈神经网络&#xff08;Feedforward Neural Network&#xff09;不同&#xff0c;RNN具有循环连接&#xff0c;使得网络能…

【Java】-【IDEA 编译项目时报错:GC overhead limit exceeded】

文章目录 问题简述问题分析解决方案解决方案一解决方案二 问题简述 以前项目启动都是好好的&#xff0c;没有任何问题&#xff0c;最近启动时却报错&#xff1a; java.lang.OutOfMemoryError: ...(此处忽略) GC overhead limit exceeded问题分析 错误是发生在编译阶段&#…

STM32单片机CAN总线汽车灯光控制系统远近光刹车双闪

实践制作DIY- GC0151---CAN总线汽车灯光控制系统 基于STM32单片机设计---CAN总线汽车灯光控制系统 二、功能介绍&#xff1a; OLED主控板&#xff1a;STM32F103C系列最小系统OLED显示3个按键&#xff08;大灯开关、大灯自动/手动、大灯近光/远光&#xff09;左转按键右转按键双…

docker学习(七)docker daemon

1.Docker 的CS模式 1.1.Docker 的C/S模式介绍 在 Docker Client 中来运行 Docker 的各种命令&#xff0c;这些命令会传送给在 Docker 的宿主机上运行的 Docker 守护进程。而 Docker 守护进程是负责实现 Docker 各种功能的。 如图所示&#xff0c;Docker 守护进程运行在宿主机…

超参数调试、Batch正则化和程序框架

1、归一化网络的激活函数&#xff08;Normalizing activations in a network &#xff09; 规范化&#xff0c;方法如下&#xff0c;减去均值再除以标准偏差&#xff0c;为了使数值稳定&#xff0c; 通常将&#x1d700;作为分母&#xff0c;以防&#x1d70e; 0的情况 &…

使用maven profile 实现一次打包多版本依赖的fat jar

基于一种特殊情况: 需要开发通用代码,但底层依赖的jar有不同版本,使用一次maven 命令编译来同时生成多个fat jar。 测试代码结构: log4j-v1/log4j-v2 有一个同名类被maintest引用。 maintest pom.xml如下 <?xml version="1.0" encoding="UTF-8"…

Docker的安装以及Docker私有仓库的搭建

Docker的安装 # 1、yum 包更新到最新 yum update # 2、安装需要的软件包&#xff0c; yum-util 提供yum-config-manager功能&#xff0c;另外两个是devicemapper驱动依赖的 yum install -y yum-utils device-mapper-persistent-data lvm2 # 3、 设置yum源 yum-config-manage…

基于QT设计的无人机地面站(摄像头录像拍摄)

一、功能需求 通过QT设计一款无人机地面站软件,需要包含基本的RTSP拉流功能,对接无人机平台的RTSP流。此外,需要完成拍照、录像、OSD叠加功能;完成按钮控制云台进行拍照、录像、变焦、指点运动等。在此基础上,完成对应的目标跟踪识别。 技术要求 (1)采用QT平台,设计W…

一、QT主界面简介和创建一个QT工程

QT从入门到实战学习笔记 一、QtCreator主界面简介1、欢迎界面2、编辑界面3、设计界面4、Debug界面5、帮助界面6、主界面左下角按钮 二、创建QT工程1、创建一个新的project2、模板选择3、项目介绍和位置4、定义所建的系统文件类型5、创建类信息&#xff08;Details&#xff09;6…

Redis【实战篇】---- 秒杀优化

Redis【实战篇】---- 秒杀优化 1. 秒杀优化-异步秒杀思路2. 秒杀优化-Redis完成秒杀资格判断3. 秒杀优化-基于阻塞队列完成秒杀优化 1. 秒杀优化-异步秒杀思路 我们来回顾一下下单流程 当用户发起请求&#xff0c;此时会请求nginx&#xff0c;nginx会访问到tomcat&#xff0c…