boost::interprocess::shared_ptr 的共享生命周期跨进程
Shared lifecycle cross processes with boost::interprocess::shared_ptr
我正在尝试使用 boost::interprocess 以便在进程之间共享数据并利用 shared_ptr 进行生命周期管理。我在共享内存中有一个映射,应该有两个进程访问它。
boost::shared_ptr<boost::interprocess::managed_mapped_file> segment =
boost::make_shared<boost::interprocess::managed_mapped_file>
(boost::interprocess::open_or_create,
"./some-mmap.txt", //file name
65536); //segment size in bytes
pair_allocator_type alloc_inst(segment->get_segment_manager());
elements = boost::interprocess::make_managed_shared_ptr(
segment->find_or_construct<map_type>("elements")
(std::less<IdType>(), alloc_inst),
*segment
);
在我的测试程序中,一个 Parent 和一个 Child 进程基本上都使用上面的代码段。因此,它们使用相同的底层文件、相同的共享名称object("elements")、相同的类型等
但是,我注意到每当 child 进程终止时,collection 的大小就会下降到 0。奇怪。我调查了一下,它似乎与 elements
的破坏有关(当这个共享指针超出范围时)。每当 elements
超出范围时,基础 collection 的大小变为 0。
我还看到 elements
在 Parent 和 Child 过程中都有 use_count
正好 1。对于 Parent 这是有道理的,但我不明白为什么 Child 是这样。我的假设是当Child进程死掉时,use_count降为0,然后collection被清除。
我想要的是当 Child 进程死亡时,指向的 object (地图)不会被破坏。我不应该假设哪些进程是活动的,哪些不是。
- 我是否以错误的方式初始化
boost::interprocess::shared_ptr
?
- 我是否完全遗漏了这个指针的语义 --> 它是否仅用于在一个进程中管理 shared-memory object 的生命周期 仅 并且不是跨进程?
- 如何让 shared_ptr 的 use_count 跨进程共享?
编辑 - 关于 collection
的说明
elements
是一个 boost::interprocess::map
,它将某个 IdType
映射到一个指向 ShmemType
的 shared-memory 共享指针。当 Child 进程终止时,elements
的大小下降到 0。
typedef boost::interprocess::managed_mapped_file::segment_manager segment_manager_type;
typedef std::pair<const IdType, ShmemType::pointer_type> pair_type;
typedef boost::interprocess::allocator<pair_type, segment_manager_type> pair_allocator_type;
typedef boost::interprocess::map<IdType, ShmemType::pointer_type, std::less<IdType>, pair_allocator_type> map_type;
编辑 - 来自 boost 文档的示例
我从 boost 文档中获取示例并对其进行扩展以找出我最初问题的根本原因。
#include <boost/interprocess/managed_mapped_file.hpp>
#include <boost/interprocess/containers/vector.hpp>
#include <boost/interprocess/smart_ptr/shared_ptr.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <string>
#include <cstdlib> //std::system
using namespace boost::interprocess;
typedef allocator<int, managed_mapped_file::segment_manager> ShmemAllocator;
typedef vector<int, ShmemAllocator> MyVector;
#include <iostream>
//Main function. For parent process argc == 1, for child process argc == 2
int main(int argc, char *argv[])
{
if(argc == 1){ //Parent process
//Create a new segment with given name and size
managed_mapped_file segment(open_or_create, "./a_MySharedMemory.txt", 65536);
//Initialize shared memory STL-compatible allocator
const ShmemAllocator alloc_inst (segment.get_segment_manager());
// MyVector* elements = segment.find_or_construct<MyVector>("some-vector") //object name
// (alloc_inst);
typedef boost::interprocess::managed_shared_ptr<MyVector, boost::interprocess::managed_mapped_file>::type map_pointer_type;
map_pointer_type elements = boost::interprocess::make_managed_shared_ptr(
segment.find_or_construct<MyVector>("some-vector") //object name
(alloc_inst),
segment
);
for(int i = 0; i < 100; ++i) //Insert data in the vector
elements->push_back(i);
std::cout << elements->size() << std::endl;
std::cout << elements->at(0) << std::endl;
std::cout << elements->at(30) << std::endl;
//Launch child process
std::string s(argv[0]); s += " child ";
if(0 != std::system(s.c_str()))
return 1;
std::cout << elements->size() << std::endl;
std::cout << elements->at(0) << std::endl;
std::cout << elements->at(30) << std::endl;
}
else{ //Child process
//Open the managed segment
managed_mapped_file segment(open_only, "./a_MySharedMemory.txt");
const ShmemAllocator alloc_inst (segment.get_segment_manager());
typedef boost::interprocess::managed_shared_ptr<MyVector, boost::interprocess::managed_mapped_file>::type map_pointer_type;
map_pointer_type elements = boost::interprocess::make_managed_shared_ptr(
segment.find_or_construct<MyVector>("some-vector") //object name
(alloc_inst),
segment
);
// MyVector* elements = segment.find_or_construct<MyVector>("some-vector") //object name
// (alloc_inst);
//Use vector in reverse order
std::sort(elements->rbegin(), elements->rend());
}
return 0;
}
在这种情况下,在 child 进程结束后,向量在 parent 进程中的大小 == 0。如果我使用原始指针 (MyVector* elements = segment.find_or_construct...
),那么 collection 可以在 parent 进程中按预期使用。
所以我对共享指针的行为仍有疑问
- 问: 我是否以错误的方式初始化了
boost::interprocess::shared_ptr
?
你做得对。不过,您不需要共享指针中的段。只需确保该段比任何进程间shared_ptrs(或者,就此而言,任何对共享内存段的引用)都存在。
- 问: 我是否完全遗漏了这个指针的语义 --> 它仅用于管理一个进程中的共享内存对象生命周期并且不是跨进程?
没有
- 问: 如何让一个
shared_ptr
的use_count
跨进程共享?
Returns an instance of a shared pointer constructed with the default allocator and deleter from a pointer of type T that has been allocated in the passed managed segment.
other page明确提到(强调我的):
Since the reference count and other auxiliary data needed by shared_ptr must be created also in the managed segment, and the deleter has to delete the object from the segment, the user must specify an allocator object and a deleter object when constructing a non-empty instance of shared_ptr
我现在看到,如果我在子进程死后尝试在共享内存中的父进程中创建任何对象,我会收到断言错误:
assertion "hdr->m_value_alignment == algn" failed:
file "/usr/include/boost/interprocess/detail/segment_manager_helper.hpp", line 181,
function: static boost::interprocess::detail::block_header* boost::interprocess::detail::block_header::block_header_from_value(const void*, size_t, size_t)
所以现在我怀疑这是我从父进程生成子进程的方式。
//Launch child process
std::string s(argv[0]); s += " child";
std::cout << "Child about to be born" << std::endl;
if(0 != std::system(s.c_str())){
std::cout << "(Parent): Child failed!" << std::endl;
return 1;
}
我设法解决了这个问题。该问题与共享指针的制作方式有关
如果您调用 boost::interprocess::make_managed_shared_ptr
N 次以在共享内存中创建指向某个对象的共享指针,您将获得指向 相同 的根本不同(完全不相关)的共享指针共享内存中的对象。我在 Parent 和 Child 进程中这样做,然后当 Child 进程死亡时,使用计数变为 0 并擦除指向的对象(地图)。
解决方案是显式创建命名共享指针。
typedef boost::interprocess::allocator<void, segment_manager_type> void_allocator_type;
typedef boost::interprocess::deleter<map_type, segment_manager_type> map_deleter_type;
typedef boost::interprocess::shared_ptr<map_type, void_allocator_type, map_deleter_type> map_pointer_type;
segment_manager_type* segment_manager = segment->get_segment_manager();
pair_allocator_type alloc_inst(segment_manager);
segment->construct<map_pointer_type>("elements ptr")(
segment->construct<map_type>("elements")(std::less<IdClass>(), alloc_inst), //object to own
void_allocator_type(segment_manager), //allocator
map_deleter_type(segment_manager) //deleter
);
然后在Child进程中只得到这个指针的副本
map_pointer_type elements = *segment->find<map_pointer_type>("elements ptr").first;
我正在尝试使用 boost::interprocess 以便在进程之间共享数据并利用 shared_ptr 进行生命周期管理。我在共享内存中有一个映射,应该有两个进程访问它。
boost::shared_ptr<boost::interprocess::managed_mapped_file> segment =
boost::make_shared<boost::interprocess::managed_mapped_file>
(boost::interprocess::open_or_create,
"./some-mmap.txt", //file name
65536); //segment size in bytes
pair_allocator_type alloc_inst(segment->get_segment_manager());
elements = boost::interprocess::make_managed_shared_ptr(
segment->find_or_construct<map_type>("elements")
(std::less<IdType>(), alloc_inst),
*segment
);
在我的测试程序中,一个 Parent 和一个 Child 进程基本上都使用上面的代码段。因此,它们使用相同的底层文件、相同的共享名称object("elements")、相同的类型等
但是,我注意到每当 child 进程终止时,collection 的大小就会下降到 0。奇怪。我调查了一下,它似乎与 elements
的破坏有关(当这个共享指针超出范围时)。每当 elements
超出范围时,基础 collection 的大小变为 0。
我还看到 elements
在 Parent 和 Child 过程中都有 use_count
正好 1。对于 Parent 这是有道理的,但我不明白为什么 Child 是这样。我的假设是当Child进程死掉时,use_count降为0,然后collection被清除。
我想要的是当 Child 进程死亡时,指向的 object (地图)不会被破坏。我不应该假设哪些进程是活动的,哪些不是。
- 我是否以错误的方式初始化
boost::interprocess::shared_ptr
? - 我是否完全遗漏了这个指针的语义 --> 它是否仅用于在一个进程中管理 shared-memory object 的生命周期 仅 并且不是跨进程?
- 如何让 shared_ptr 的 use_count 跨进程共享?
编辑 - 关于 collection
的说明elements
是一个 boost::interprocess::map
,它将某个 IdType
映射到一个指向 ShmemType
的 shared-memory 共享指针。当 Child 进程终止时,elements
的大小下降到 0。
typedef boost::interprocess::managed_mapped_file::segment_manager segment_manager_type;
typedef std::pair<const IdType, ShmemType::pointer_type> pair_type;
typedef boost::interprocess::allocator<pair_type, segment_manager_type> pair_allocator_type;
typedef boost::interprocess::map<IdType, ShmemType::pointer_type, std::less<IdType>, pair_allocator_type> map_type;
编辑 - 来自 boost 文档的示例
我从 boost 文档中获取示例并对其进行扩展以找出我最初问题的根本原因。
#include <boost/interprocess/managed_mapped_file.hpp>
#include <boost/interprocess/containers/vector.hpp>
#include <boost/interprocess/smart_ptr/shared_ptr.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <string>
#include <cstdlib> //std::system
using namespace boost::interprocess;
typedef allocator<int, managed_mapped_file::segment_manager> ShmemAllocator;
typedef vector<int, ShmemAllocator> MyVector;
#include <iostream>
//Main function. For parent process argc == 1, for child process argc == 2
int main(int argc, char *argv[])
{
if(argc == 1){ //Parent process
//Create a new segment with given name and size
managed_mapped_file segment(open_or_create, "./a_MySharedMemory.txt", 65536);
//Initialize shared memory STL-compatible allocator
const ShmemAllocator alloc_inst (segment.get_segment_manager());
// MyVector* elements = segment.find_or_construct<MyVector>("some-vector") //object name
// (alloc_inst);
typedef boost::interprocess::managed_shared_ptr<MyVector, boost::interprocess::managed_mapped_file>::type map_pointer_type;
map_pointer_type elements = boost::interprocess::make_managed_shared_ptr(
segment.find_or_construct<MyVector>("some-vector") //object name
(alloc_inst),
segment
);
for(int i = 0; i < 100; ++i) //Insert data in the vector
elements->push_back(i);
std::cout << elements->size() << std::endl;
std::cout << elements->at(0) << std::endl;
std::cout << elements->at(30) << std::endl;
//Launch child process
std::string s(argv[0]); s += " child ";
if(0 != std::system(s.c_str()))
return 1;
std::cout << elements->size() << std::endl;
std::cout << elements->at(0) << std::endl;
std::cout << elements->at(30) << std::endl;
}
else{ //Child process
//Open the managed segment
managed_mapped_file segment(open_only, "./a_MySharedMemory.txt");
const ShmemAllocator alloc_inst (segment.get_segment_manager());
typedef boost::interprocess::managed_shared_ptr<MyVector, boost::interprocess::managed_mapped_file>::type map_pointer_type;
map_pointer_type elements = boost::interprocess::make_managed_shared_ptr(
segment.find_or_construct<MyVector>("some-vector") //object name
(alloc_inst),
segment
);
// MyVector* elements = segment.find_or_construct<MyVector>("some-vector") //object name
// (alloc_inst);
//Use vector in reverse order
std::sort(elements->rbegin(), elements->rend());
}
return 0;
}
在这种情况下,在 child 进程结束后,向量在 parent 进程中的大小 == 0。如果我使用原始指针 (MyVector* elements = segment.find_or_construct...
),那么 collection 可以在 parent 进程中按预期使用。
所以我对共享指针的行为仍有疑问
- 问: 我是否以错误的方式初始化了
boost::interprocess::shared_ptr
?
你做得对。不过,您不需要共享指针中的段。只需确保该段比任何进程间shared_ptrs(或者,就此而言,任何对共享内存段的引用)都存在。
- 问: 我是否完全遗漏了这个指针的语义 --> 它仅用于管理一个进程中的共享内存对象生命周期并且不是跨进程?
没有
- 问: 如何让一个
shared_ptr
的use_count
跨进程共享?
Returns an instance of a shared pointer constructed with the default allocator and deleter from a pointer of type T that has been allocated in the passed managed segment.
other page明确提到(强调我的):
Since the reference count and other auxiliary data needed by shared_ptr must be created also in the managed segment, and the deleter has to delete the object from the segment, the user must specify an allocator object and a deleter object when constructing a non-empty instance of shared_ptr
我现在看到,如果我在子进程死后尝试在共享内存中的父进程中创建任何对象,我会收到断言错误:
assertion "hdr->m_value_alignment == algn" failed:
file "/usr/include/boost/interprocess/detail/segment_manager_helper.hpp", line 181,
function: static boost::interprocess::detail::block_header* boost::interprocess::detail::block_header::block_header_from_value(const void*, size_t, size_t)
所以现在我怀疑这是我从父进程生成子进程的方式。
//Launch child process
std::string s(argv[0]); s += " child";
std::cout << "Child about to be born" << std::endl;
if(0 != std::system(s.c_str())){
std::cout << "(Parent): Child failed!" << std::endl;
return 1;
}
我设法解决了这个问题。该问题与共享指针的制作方式有关
如果您调用 boost::interprocess::make_managed_shared_ptr
N 次以在共享内存中创建指向某个对象的共享指针,您将获得指向 相同 的根本不同(完全不相关)的共享指针共享内存中的对象。我在 Parent 和 Child 进程中这样做,然后当 Child 进程死亡时,使用计数变为 0 并擦除指向的对象(地图)。
解决方案是显式创建命名共享指针。
typedef boost::interprocess::allocator<void, segment_manager_type> void_allocator_type;
typedef boost::interprocess::deleter<map_type, segment_manager_type> map_deleter_type;
typedef boost::interprocess::shared_ptr<map_type, void_allocator_type, map_deleter_type> map_pointer_type;
segment_manager_type* segment_manager = segment->get_segment_manager();
pair_allocator_type alloc_inst(segment_manager);
segment->construct<map_pointer_type>("elements ptr")(
segment->construct<map_type>("elements")(std::less<IdClass>(), alloc_inst), //object to own
void_allocator_type(segment_manager), //allocator
map_deleter_type(segment_manager) //deleter
);
然后在Child进程中只得到这个指针的副本
map_pointer_type elements = *segment->find<map_pointer_type>("elements ptr").first;