在 C++ 中,共享内存(Shared Memory)是一种进程间通信(IPC)机制,允许多个进程访问同一块内存区域,从而实现高效的数据共享。以下是共享内存的用法、使用场景及相关注意事项,格式为 Markdown:


共享内存的用法

C++ 中使用共享内存通常依赖操作系统提供的 API(如 POSIX 的 shm_openmmap,或 Windows 的 CreateFileMappingMapViewOfFile)。C++11 及以上版本没有直接提供标准库支持,但可以通过系统调用或第三方库(如 Boost.Interprocess)实现。以下以 POSIX 系统为例说明用法:

1. 创建/打开共享内存

使用 shm_open 创建或打开一个共享内存对象,并通过 mmap 将其映射到进程地址空间。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <iostream>
#include <cstring>

int main() {
// 打开或创建共享内存对象
const char* shm_name = "/my_shared_memory";
int shm_fd = shm_open(shm_name, O_CREAT | O_RDWR, 0666);
if (shm_fd == -1) {
std::cerr << "shm_open failed\n";
return 1;
}

// 设置共享内存大小
size_t size = 4096; // 4KB
if (ftruncate(shm_fd, size) == -1) {
std::cerr << "ftruncate failed\n";
return 1;
}

// 映射共享内存到进程地址空间
void* ptr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
if (ptr == MAP_FAILED) {
std::cerr << "mmap failed\n";
return 1;
}

// 写入数据
strcpy(static_cast<char*>(ptr), "Hello, Shared Memory!");

// 清理
munmap(ptr, size); // 解除映射
close(shm_fd); // 关闭文件描述符
shm_unlink(shm_name); // 删除共享内存对象
return 0;
}

2. 读取共享内存

另一个进程可以通过相同的共享内存名称(shm_name)打开并映射内存,读取或修改数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <sys/mman.h>
#include <fcntl.h>
#include <iostream>

int main() {
const char* shm_name = "/my_shared_memory";
int shm_fd = shm_open(shm_name, O_RDONLY, 0666);
if (shm_fd == -1) {
std::cerr << "shm_open failed\n";
return 1;
}

size_t size = 4096;
void* ptr = mmap(0, size, PROT_READ, MAP_SHARED, shm_fd, 0);
if (ptr == MAP_FAILED) {
std::cerr << "mmap failed\n";
return 1;
}

// 读取数据
std::cout << "Shared Memory Content: " << static_cast<char*>(ptr) << '\n';

// 清理
munmap(ptr, size);
close(shm_fd);
return 0;
}

3. 同步机制

共享内存本身不提供同步机制,多进程并发访问可能导致数据竞争。需要使用信号量(sem_t)、互斥锁或原子操作确保线程安全。例如,使用 POSIX 信号量:

1
2
3
4
5
6
7
8
9
10
11
12
#include <semaphore.h>

// 初始化信号量
sem_t* sem = sem_open("/my_semaphore", O_CREAT, 0666, 1);
// 等待信号量
sem_wait(sem);
// 访问共享内存...
// 释放信号量
sem_post(sem);
// 清理
sem_close(sem);
sem_unlink("/my_semaphore");

4. 使用 Boost.Interprocess(跨平台)

Boost 提供高级接口,简化共享内存管理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <iostream>

using namespace boost::interprocess;

int main() {
// 创建共享内存
shared_memory_object shm(create_only, "MySharedMemory", read_write);
shm.truncate(4096); // 设置大小

// 映射
mapped_region region(shm, read_write);

// 写入数据
std::string message = "Hello, Boost Shared Memory!";
std::memcpy(region.get_address(), message.c_str(), message.size());

// 清理(自动解除映射)
shared_memory_object::remove("MySharedMemory");
return 0;
}

使用场景

  1. 高性能数据共享

    • 多个进程需要快速交换大量数据(如图像、日志、配置)。
    • 例:实时视频处理,共享帧缓冲区。
  2. 进程间通信

    • 服务器与客户端共享状态或缓存(如数据库连接池)。
    • 例:Web 服务器与工作进程共享会话数据。
  3. 并行计算

    • 多进程并行处理大数据集,共享输入/输出缓冲区。
    • 例:科学计算中,多个进程处理同一矩阵。
  4. 低延迟通信

    • 相比管道或消息队列,共享内存避免数据拷贝,适合低延迟场景。
    • 例:高频交易系统中的订单簿共享。
  5. 跨语言/跨进程交互

    • 不同语言编写的进程(如 C++ 和 Python)通过共享内存交换数据。
    • 例:机器学习模型推理,C++ 预处理数据后供 Python 使用。

注意事项

  1. 同步问题

    • 必须使用同步机制(如信号量、互斥锁)避免数据竞争。
    • 推荐 Boost.Interprocess 提供的同步工具。
  2. 内存管理

    • 确保所有进程正确解除映射(munmap)并删除共享内存(shm_unlink)。
    • 避免内存泄漏或悬空指针。
  3. 跨平台兼容性

    • POSIX API(如 shm_open)适用于 Linux/Unix,Windows 使用 CreateFileMapping
    • 使用 Boost.Interprocess 可提高代码可移植性。
  4. 安全性

    • 设置适当权限(如 0666)以控制访问。
    • 避免未初始化的共享内存导致未定义行为。
  5. 性能开销

    • 共享内存创建和映射有初始开销,适合长期使用。
    • 频繁创建/销毁共享内存可能不如其他 IPC 机制(如管道)高效。

数学表示

  • 内存访问时间:共享内存的访问时间接近本地内存,为 ( O(1) ),无数据拷贝开销。
  • 同步开销:信号量或锁的加锁/解锁时间通常为 ( O(1) ),但竞争激烈时可能退化为 ( O(n) ),其中 ( n ) 为竞争进程数。
  • 空间复杂度:共享内存大小为 ( S ),由 ftruncatetruncate 设定,额外同步机制(如信号量)占用 ( O(1) ) 空间。

适用场景与替代方案

  • 适用场景:需要高效、大量数据共享的进程间通信。
  • 替代方案
    • 管道/消息队列:适合小数据量或流式传输。
    • 套接字:适合网络通信或跨主机场景。
    • 文件映射:适合持久化数据共享。