boost库学习:其一、共享内存

boost库本身不是系统基本的api,主要的贡献是封装原有的系统基本接口。linux下面的共享内存使用很繁琐,推荐使用boost库里面的共享内存接口,底层是POSIX的共享内存。

一、常用接口

常用接口是以下几个:
1、shared_memory_object 类

shared_memory_object shdmem{open_or_create, "Boost", read_write};

这个类用来创建共享内存标识,这个类创建好之后,/dev/shm下面会产生一个文件,文件名就是共享内存名。下面介绍构造函数的三个参数:
(1)参数1:表示共享内存标识的创建方式。
open_or_create:表示如果共享内存标识存在,那么直接打开;如果共享内存标识不存在,那么直接新建。
open_only:如果共享内存存在,那么直接打开;如果共享内存不存在,直接core dump!
create_only:如果共享内存不存在,那么直接打开;如果共享内存存在,直接core dump!
所以,如果最好用open_or_create创建。

(2)参数2:共享内存名。会在/dev/shm下面创建同名文件。
(3)读写模式,这个一般设置成read_write。
2、truncate函数
truncate(size_t size)是shared_memory_object 的普通成员函数,表示要创建多少字节的共享内存。
3、mapped_region类

mapped_region region{shdmem, read_write};

mapped_region类用于进程映射共享内存,构造函数里面有两个参数,第一个参数是shared_memory_object 对象,第二个参数是读写方式。
之后调用region.get_address()可以返回共享内存地址。
4、remove函数
这个函数用于删除共享内存,如果没有进程调用这个函数,那么共享内存无法删除。
如果删除多了其实没有什么问题,这个函数带返回值,表示删除成功与否,无视返回值就好了。

二、使用示例

进程A,先启动

#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <iostream>
#include <unistd.h>

using namespace boost::interprocess;

int main()
{
  shared_memory_object shdmem{open_or_create, "Boost", read_write};
  shdmem.truncate(1024);
  mapped_region region{shdmem, read_write};
  int *i1 = static_cast<int*>(region.get_address());
  *i1 = 99;
  sleep(30);
  bool removed = shared_memory_object::remove("Boost");
  std::cout<<removed<<std::endl;
}

进程B,后启动

#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <iostream>
#include <unistd.h>

using namespace boost::interprocess;

int main()
{
  shared_memory_object shdmem{open_or_create, "Boost", read_write};
  shdmem.truncate(2048);
  mapped_region region2{shdmem, read_only};
  int *i2 = static_cast<int*>(region2.get_address());
  std::cout << *i2 << '\n';
  bool removed = shared_memory_object::remove("Boost");
  std::cout<<removed<<std::endl;
}

我故意把两个进程映射的内存大小设置成不一样的,这是无伤大雅的。结果就是只有前1024个字节可以用于进程通信。
进程A输出:

0

他删除共享内存失败了,因为已经被进程B删除了,但是正常退出,没有崩溃。

进程B输出:

99
1

正常获取到了数据,而且正常删除了共享内存。

三、不手动删除共享内存

先执行进程A

#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <iostream>
#include <unistd.h>

using namespace boost::interprocess;

int main()
{
  shared_memory_object shdmem{open_or_create, "Boost", read_write};
  shdmem.truncate(1024);
  mapped_region region{shdmem, read_write};
  int *i1 = static_cast<int*>(region.get_address());
  *i1 = 99;
}

没有删除共享内存。可以看到/dev/shm/Boost一直存在。
进程A退出后执行进程B:

#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <iostream>
#include <unistd.h>

using namespace boost::interprocess;

int main()
{
  shared_memory_object shdmem{open_or_create, "Boost", read_write};
  shdmem.truncate(2048);
  mapped_region region2{shdmem, read_only};
  int *i2 = static_cast<int*>(region2.get_address());
  std::cout << *i2 << '\n';
  bool removed = shared_memory_object::remove("Boost");
  std::cout<<removed<<std::endl;
}

可以输出99,说明读取了残留数据。但是也说明打开存在的共享内存不会报错。

四、如何保证共享内存被回收?

这在linux下面其实很难办,因为linux下面的共享内存还是封装的POSIX的shmget函数,linux共享内存没有windows的功能那么强大。
好在,linux下boost共享内存对多次删除操作不敏感,只要进程是正常退出的,共享内存都有办法清理。
唯一的问题是,异常退出该怎么办?
其实可以借助linux的信号注册机制处理这个问题。
简单地说,就是我们向系统注册一个函数,进程收到某个信号后,会触发这个函数,这个函数可以是段错误、零除错误、ctrl+c退出信号,等等。
可以在注册函数里面,加上对共享内存的清除操作,如果进程发生了core dump或者被人为终止,都可以清理共享内存。代码示例如下:

#include <boost/interprocess/shared_memory_object.hpp>
#include <cstdlib>
#include <iostream>
#include <csignal>
namespace bip = boost::interprocess;

void cleanup_shm(int signal) {
    bip::shared_memory_object::remove("MyShm");
    std::cout << "Cleanup: Shared memory removed." << std::endl;
    std::exit(EXIT_SUCCESS);
}

int main() {
    // 注册信号处理函数
    std::signal(SIGFPE, cleanup_shm);  // 零除错误
    std::signal(SIGINT, cleanup_shm);   // Ctrl+C
    std::signal(SIGSEGV, cleanup_shm);  // 段错误

    // 创建共享内存
    bip::shared_memory_object shm(bip::open_or_create, "MyShm", bip::read_write);
    shm.truncate(1024);

    // ... 使用共享内存 ...
    int * p = NULL;
    *p = 1;
    //或者是:
    //int a = 1/0;
    //或者是:sleep过程中ctrl+c退出
    return 0;  
}

经过测试,即使异常退出也可以正常删除共享内存。

五、待讨论点

(1)信号注册的方法,对kill -9无效。
(2)对于boost的共享内存,官方提供的最新版是managed_shared_memory ,但是这个接口感觉太不灵活了,就没讨论。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值