【进程间通信】——共享内存基础

目录

使用共享内存传递uint8 data和uint16 len,将该共享内存操作写成动态库,便于被其它程序调用。

一、基于共享内存的动态库

1.1 源码

头文件shared_memory.h内容如下,通过结构体SharedData定义了传输的消息类型,注意后续因为要设置传输消息类型的内存大小,需要用到sizeof()计算内存大小,因此最好把要传输的数据放到结构体中,便于计算。

// shared_memory.h
#ifndef SHARED_MEMORY_H
#define SHARED_MEMORY_H

#include <cstdint>

typedef uint8_t uint8;
typedef uint16_t uint16;

struct SharedData {
    uint8 data;
    uint16 len;
};

void create_shared_memory();
void destroy_shared_memory();
SharedData* get_shared_data();

#endif // SHARED_MEMORY_H

函数定义文件shared_memory.cpp主要包含创建内存空间、销毁内存空间、返回内存空间的地址。

// shared_memory.cpp
// g++ -shared -fPIC shared_memory.cpp -o libshared_memory.so

#include "shared_memory.h"
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <iostream>

const char* SHM_NAME = "/shared_memory_example";
int shm_fd = -1;
SharedData* shared_data_ptr = nullptr;

void create_shared_memory() {
    shm_fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0666);
    if (shm_fd == -1) {
        std::cerr << "Failed to create shared memory." << std::endl;
        return;
    }

    if (ftruncate(shm_fd, sizeof(SharedData)) == -1) {
        std::cerr << "Failed to set shared memory size." << std::endl;
        return;
    }

    shared_data_ptr = static_cast<SharedData*>(mmap(nullptr, sizeof(SharedData), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0));
    if (shared_data_ptr == MAP_FAILED) {
        std::cerr << "Failed to map shared memory." << std::endl;
        return;
    }
}

void destroy_shared_memory() {
    if (shared_data_ptr) {
        munmap(shared_data_ptr, sizeof(SharedData));
    }

    if (shm_fd != -1) {
        close(shm_fd);
        shm_unlink(SHM_NAME);
    }
}

SharedData* get_shared_data() {
    return shared_data_ptr;
}

1.2 编译命令

g++ -shared -fPIC shared_memory.cpp -o libshared_memory.so

注意其它程序要动态链接该库,需要添加动态库寻找的路径:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.

最后的.代表动态库处于当前目录。

1.3 函数解释

1.3.1 shm_open()函数

shm_open是一个POSIX共享内存API的函数,用于创建或打开一个命名的共享内存对象。这个函数的原型如下:
int shm_open(const char *name, int oflag, mode_t mode);

参数:

  • name:共享内存对象的名称,通常以一个斜杠(/)开头。
  • oflag:打开或创建共享内存对象的标志,如O_CREAT(创建)、O_RDWR(读写模式)等。
  • mode:设置共享内存对象的权限,如S_IRUSR(用户读权限)和S_IWUSR(用户写权限)等。
  • 返回值:成功时返回一个非负整数的文件描述符,失败时返回-1。

1.3.2 ftruncate()函数

ftruncate函数用于调整文件大小,通常用于设置共享内存对象的大小。这个函数的原型如下:
int ftruncate(int fd, off_t length);

参数:

  • fd:文件描述符,通常是shm_open函数的返回值。
  • length:文件的新大小。
  • 返回值:成功时返回0,失败时返回-1。

1.3.3 mmap()函数

mmap函数用于将一个文件或共享内存对象映射到进程的虚拟地址空间。这个函数的原型如下:
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

参数:

  • addr:建议的映射起始地址,通常设置为NULL,让系统自动选择。
  • length:映射区域的大小。
  • prot:映射区域的保护属性,如PROT_READ(可读)、PROT_WRITE(可写)等。
  • flags:映射类型,如MAP_SHARED(共享映射)或MAP_PRIVATE(私有映射)等。
  • fd:文件描述符,通常是shm_open函数的返回值。
  • offset:文件偏移量。
  • 返回值:成功时返回映射区域的起始地址,失败时返回MAP_FAILED。

1.3.4 munmap()函数

munmap函数用于解除进程虚拟地址空间的映射。这个函数的原型如下:
int munmap(void *addr, size_t length);

参数:

  • addr:映射区域的起始地址。
  • length:映射区域的大小。
  • 返回值:成功时返回0,失败时返回-1。

1.3.5 shm_unlink()函数

shm_unlink函数用于删除一个命名的共享内存对象。当共享内存对象的引用计数降至0时,它会被系统释放。这个函数的原型如下:

int shm_unlink(const char *name);

参数:

  • name:共享内存对象的名称,通常与shm_open函数中使用的相同。
  • 返回值:成功时返回0,失败时返回-1。

1.3.6 close()函数

close函数用于关闭一个文件描述符。这个函数的原型如下:

int close(int fd);

参数:

  • fd:要关闭的文件描述符。
  • 返回值:成功时返回0,失败时返回-1。

二、sender.cpp

2.1 源码

// sender.cpp
// g++ sender.cpp -o sender -L. -lshared_memory -lrt
// export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
#include "shared_memory.h"
#include <iostream>

int main() {
    create_shared_memory();
    SharedData* shared_data = get_shared_data();

    if (shared_data) {
        shared_data->data = 42;
        shared_data->len = 16;
        std::cout << "Set shared data: " << static_cast<int>(shared_data->data) << ", " << shared_data->len << std::endl;
    } else {
        std::cerr << "Failed to access shared memory." << std::endl;
    }
    char ch = 0;
    ch = getchar();
    destroy_shared_memory();
    return 0;
}

2.2 编译运行命令

编译:
g++ sender.cpp -o sender -L. -lshared_memory -lrt

运行:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
./sender

注意运行该文件时,需要export命令添加共享内存动态库的路径。

2.3 源码解释

sender通过create_shared_memory()函数创建了共享内存,其中共享内存的名称已经在动态库中设置为全局变量:
const char* SHM_NAME = "/shared_memory_example";

接着通过get_shared_data()函数获取该名称对应的共享内存地址(每个共享内存的名称对应的地址是唯一的)。

接着对该共享内存中的数据成员进行赋值操作。

        shared_data->data = 42;
        shared_data->len = 16;

由于需要等待receiver读取共享内存后才能进行销毁,因此这里通过getchar()函数阻塞进程,保持进程存活,待receiver读取后,在send中输入回车按键即可退出。

三、receiver.cpp

3.1 源码

// receiver.cpp
// g++ receiver.cpp -o receiver -L. -lshared_memory -lrt
// export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
#include "shared_memory.h"
#include <iostream>

int main() {
    create_shared_memory();
    SharedData* shared_data = get_shared_data();

    if (shared_data) {
        std::cout << "Received shared data: " << static_cast<int>(shared_data->data) << ", " << shared_data->len << std::endl;
    } else {
        std::cerr << "Failed to access shared memory." << std::endl;
    }

    destroy_shared_memory();
    return 0;
}

3.2 编译运行命令

编译:
g++ receiver.cpp -o receiver -L. -lshared_memory -lrt

运行:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
./receiver

注意运行该文件时,需要export命令添加共享内存动态库的路径。

3.3 源码解释

动态库中shared_data_ptr这个全局变量在不同程序中是不共享的,因此receiver.cpp也需要运行create_shared_memory()与get_shared_data()函数通过共享内存名称来获取该共享内存地址!

    create_shared_memory();
    SharedData* shared_data = get_shared_data();

通过共享内存指针获取数据后打印出来,由于cout无法打印uint类型字符,因此将其转换为int进行打印:
std::cout << "Received shared data: " << static_cast<int>(shared_data->data) << ", " << shared_data->len << std::endl;

四、注意事项

如果sender中没有getchar()函数,运行完后销毁共享内存,进程结束,再运行receiver,会发现没有收到数据。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,657评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,889评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,057评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,509评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,562评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,443评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,251评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,129评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,561评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,779评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,902评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,621评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,220评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,838评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,971评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,025评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,843评论 2 354

推荐阅读更多精彩内容