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

2023-11-18

个人博客地址: 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目录下新增对应两个共享内存文件

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux多进程数据交换--共享内存 的相关文章

  • JDK9下载、安装和配置环境变量图解

    下载 1 输入 https www oracle com 2 选择 java download 3 点击 java archive 下拉找到 java9 4 点击进入 选择windows版本 5 勾选 下载 需要账号下载 安装 1 双击安装
  • 瞎写

    有人说人生有两大禁忌 一忌踌躇满志 一忌心灰意冷 别人我不知道 但是对我来说 似乎一直都在这两种情绪之间跳转 说实话写这篇文章的此时我应该是处于心灰意冷这个点的 下面就随便说说当处于这个点时 自己产生的一些想法 首先 处于这个状态时整个人肯
  • 传统目标检测方法研究(一)

    1 传统算法目标检测 区域选择 gt 特征提取 gt 特征分类 1 1 区域选择 python 实现 图像滑动窗口 区域选取 首先选取图像中可能出现物体的位置 由于物体位置 大小都不固定 因此传统算法通常使用滑动窗口 Sliding Win
  • db2中各个类型互相转换处理

    给数字左侧自动占位补零 digits函数 digits 参数 digits里的参数必须是整型参数 可以是smallint int bigint decimal 其中smallint占5位 int占10位 bigint占19位 decimal
  • unity 坐标系

    https blog csdn net qq 34536551 article details 90269908
  • 什么是多态?对于多态的理解....

    什么是多态 我们都知道面向对象有四个基本特性 抽象 封装 继承 多态 概括可以理解为 抽象 封装 继承 是多态的基础 多态是抽象 封装 继承的表现 多态 是Java中非常重要的一部分 简单理解 不同类的 对象 对同一消息 做出了不同的响应就

随机推荐

  • 电脑重装系统(U盘)

    Windows10 1 首先需要准备一个8G或以上的U盘 空的 2 前往浏览器下载windows安装工具 网址如下 https www microsoft com zh cn software download windows10 3 下载
  • CentOS7 挂载磁盘出错mount: /dev/sdb is write-protected, mounting mount: unknown filesystem type '(null)'

    报错信息如下 root localhost mount dev sdb image mount dev sdb is write protected mounting read only mount unknown filesystem t
  • 掌握Python的X篇_19_函数的定义与调用

    文章目录 1 函数 2 函数的定义 3 函数调用 1 函数 在Python 函数是一种基本的编程接口 因为函数的调用只关心参数输入和返回值 所以使得我们可以更好地进行编程分工 以下程序的功能是得到输入的三个数字的平方值 根据前面所学的话 可
  • Blender编程入门

    在本教程中 我们将学习一些 Blender 脚本技术 比如如何使用代码处理 操作 复制和动画网格图元 要结合所有这些技术 我们将创建一个波浪形的锥形图案 一个看起来很酷的动画 你可以将其转换为循环 GIF 我将使用bpy data模块中的一
  • 一位程序员工作10年总结的13个忠告

    原文网址 http zhangxpower iteye com blog 1145448 展望未来 总结过去10年的程序员生涯 给程序员小弟弟小妹妹们的一些总结性忠告 走过的路 回忆起来是那么曲折 把自己的一些心得体会分享给程 序员兄弟姐妹
  • oracle数据库存储过程基本语法

    oracle数据库存储过程 一 基本语法 数据类型 一 字符类型 字符串数据类型还可以依据存储空间分为固定长度类型 CHAR 和可变长度类型 varchar2 nvarchar2 两种 1 char类型 定长字符串 会用空格填充来达到器最大
  • WSL 修改默认用户

    通常可以通过以下命令来指定进入 wsl 的时候使用的用户 wsl u
  • STM32驱动HX711称重模块

    使用模块如下图所示 使用单片机为STM32C8T6 引脚DT gt PB7 SCK gt PB6 使用通道A 下面是驱动程序 void GPIO Weigh Init void GPIO InitTypeDef GPIO InitStruc
  • 设计模式-单一职责原则介绍与理解

    描述 一个类应该专注于实现一个功能 好处 便于代码复用 举例 俄罗斯方块游戏 首先可以想到的是游戏逻辑与界面的分离 也就是说逻辑一个类 界面部分一个类 这样做的好处就是我们可以复用游戏逻辑的代码 例如我们用java写了一个基于PC端的俄罗斯
  • JAVA实现压缩解压文件

    1 源码运行前准备好文件夹 2 源码 package com els modules inquiry service impl import java io File import java io FileInputStream impor
  • P2PSim中重要函数的说明

    环境 RedHat9上安装的P2Psim0 3 目的 在P2Psim使用Vivaldi协议仿真 现状 主程序代码中关于vivaldi协议的部分注释掉了 思路 从主函数分析代码 找到原因 vivaldi协议主函数是vivalditest C
  • windows server 2012R2 部署安装 hmail

    windows server 2012R2 部署安装 hmail 环境说明 系统 windows server2012 R2软件版本 hMailServer 5 6 7 B2425 exe 邮件客户端 foxmail7 2版本 加密工具 h
  • Python21天打卡Day20-可变参数、关键字参数

    在 Python 中 可变参数允许函数接受任意数量的参数 这些参数被封装成一个元组 Tuple 或列表 List 并作为参数传递给函数 Python 中有两种类型的可变参数 args 用于传递可变数量的位置参数 Positional Arg
  • 【Docker系列】从头学起 Docker——docker run 命令详解

    文章目录 作用 语法格式 docker run 执行流程 options 说明 实际例子 例一 例二 例三 例四 例五 例六 例七 例八 总结 例九 作用 创建一个新的容器并运行一个命令 语法格式 docker run OPTIONS IM
  • springboot项目打包(exe+jdk/jre+mysql)跨平台一键安装

    SpringBoot项目打包 exe jdk jre mysql 跨平台一键安装 1 Spring Boot将javaFX应用打包为jar包 1 1 pom xml安装Spring boot maven plugin
  • 【MyBatis-Plus】详解Wrappers.<T> lambdaQuery()以及常用过滤方法

    Wrappers
  • Java 动态代理作用是什么?

    主要用来做方法的增强 让你可以在不修改源码的情况下 增强一些方法 在方法执行前后做任何你想做的事情 甚至根本不去执行这个方法 因为在 InvocationHandler的invoke方法中 你可以直接获取正在调用方法对应的 Method对象
  • linux kernel --component组件用法

    kernel component组件用法 linux component组件架构分析
  • 如何用mac搭建本地svn服务器(如何将mac变成版本管理服务器)

    前言 一 搭建本地svn服务器 1 建立代码库 2 配置文件修改 3 启动本地svn服务 二 搭建过程中常见问题 如果Mac os升级到10 0以上 自带的svn不支持了怎么办 三 mac本地使用svn软件管理svn库 cornerston
  • Linux多进程数据交换--共享内存

    个人博客地址 https cxx001 gitee io 基础 在linux系统开发当中 时常需要在多个进程之间交换数据 在多个进程之间交换数据 有很多方法 但最高效的方法莫过于共享内存 linux共享内存是通过tmpfs这个文件系统来实现