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

这篇具有很好参考价值的文章主要介绍了Linux多进程数据交换--共享内存。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

个人博客地址: 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. 测试结果

  • 测试机器

Linux多进程数据交换--共享内存,个人博客导入,共享内存

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

Linux多进程数据交换--共享内存,个人博客导入,共享内存
Linux多进程数据交换--共享内存,个人博客导入,共享内存

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

Linux多进程数据交换--共享内存,个人博客导入,共享内存文章来源地址https://www.toymoban.com/news/detail-520933.html

到了这里,关于Linux多进程数据交换--共享内存的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Linux 共享内存mmap,进程通信

    进程间通信是操作系统中重要的概念之一,使得不同的进程可以相互交换数据和进行协作。其中,共享内存是一种高效的进程间通信机制,而内存映射(mmap)是实现共享内存的一种常见方法。 存储映射 I/O 是 一个磁盘文件 与 存储空间中的一个缓冲区相映射 。于是, 当从缓

    2024年02月13日
    浏览(28)
  • 【Linux】进程间通信——管道/共享内存

    进程间通信( Inter-Process Communication,简称IPC )是指不同进程之间进行数据交换和共享信息的机制和技术。在操作系统中,每个进程都是独立运行的,有自己的地址空间和数据,因此进程之间需要一种机制来进行通信,以便彼此协调工作、共享数据或者进行同步操作。 进程间

    2024年02月16日
    浏览(30)
  • 【Linux】进程间通信之共享内存

    共享内存比管道快哦~ 文章目录 前言 一、共享内存的实现原理 二、实现共享内存的代码 总结 共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的

    2024年02月03日
    浏览(35)
  • 进程间通信--共享内存详解【Linux】

    本文详细讲解了共享内存的原理和使用,并且通过实例代码角度来深度理解共享内存,下面就让我们开始吧。 数据传输:一个进程需要将它的数据发送给另一个进程 资源共享:多个进程之间共享同样的资源。 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(

    2024年02月02日
    浏览(28)
  • Linux--进程间的通信-共享内存

    前文: Linux–进程间的通信-匿名管道 Linux–进程间的通信–进程池 Linux–进程间的通信-命名管道 对于两个进程,通过在内存开辟一块空间(操作系统开辟的),进程的虚拟地址通过页表映射到对应的共享内存空间中,进而实现通信 ; 特点和作用: 高效性: 共享内存是一种

    2024年04月26日
    浏览(27)
  • 【hello Linux】进程间通信——共享内存

    目录 前言: 1. System V共享内存 1. 共享内存的理解 2. 共享内存的使用步骤 3. 共享内存的使用         1. 共享内存的创建         查看共享内存         2. 共享内存的释放         3. 共享内存的挂接         4. 共享内存的去挂接 4. 共享内存的使用示例 1. 两进

    2024年02月01日
    浏览(66)
  • 【Linux】进程间的通信之共享内存

    利用 内存共享 进行进程间的通信的原理其实分为以下几个步骤: 在物理内存中创建一块共享内存。 将共享内存链接到要通信的进程的页表中,并通过页表进行进程地址空间的映射。 进程地址空间映射完毕以后返回首个虚拟地址,以便于进程之间进行通信。 根据共享内存的

    2024年02月09日
    浏览(37)
  • 【Linux】进程间通信 -- system V共享内存

    共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据 理解: 进程间通信,是专门设计的,用来IPC 共享内存是一种通信方式,所有想通信的进程

    2024年02月16日
    浏览(33)
  • 【Linux】进程间通信——system V共享内存

    目录  写在前面的话 System V共享内存原理 System V共享内存的建立 代码实现System V共享内存 创建共享内存shmget() ftok() 删除共享内存shmctl() 挂接共享内存shmat() 取消挂接共享内存shmdt() 整体通信流程的实现          上一章我们讲了进程间通信的第一种方式 --- 管道,这一章我

    2024年02月14日
    浏览(29)
  • Linux学习记录——이십 进程间通信(2)共享内存

    system是一套标准,独立于文件系统之外,是系统专门为通信而设计出来的内核模块,称之为system V的IPC通信机制。 共享内存的主要做法也是让两个毫不相关的进程看到同一份资源。 进程的地址空间内,栈和堆区之间有个共享区,堆是向上增长,栈是向下增长,重合的那个地方

    2023年04月24日
    浏览(28)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包