一次使用多个共享内存实例
using multiple instances of shared memory at once
为了在录制程序和显示程序(不能相同)之间传输视频流,我使用共享内存。
为了同步访问,我将 class 放在一起,其中包含 shared_memory_object、mapped_region 和 interprocess_sharable_mutex(所有 boost::interprocess)
我写了 2 个构造函数,一个用于 "Host" 端,一个用于 "Client" 端。
当我使用我的 class 传输一个视频流时,它工作得很好。
但是当我尝试传输两个视频流时出现了一些问题。
首先:这里是构造函数代码:
(第一个是Host-Constructor,第二个是Client)
template<typename T>
SWMRSharedMemArray<T>::SWMRSharedMemArray(std::string Name, size_t length):
ShMutexSize(sizeof(interprocess_sharable_mutex)),
isManager(true), _length(length), Name(Name)
{
shared_memory_object::remove(Name.c_str());
shm = new shared_memory_object(create_only, Name.c_str(), read_write);
shm->truncate(ShMutexSize + sizeof(T)*length);
region = new mapped_region(*shm, read_write);
void *addr = region->get_address();
mtx = new(addr) interprocess_sharable_mutex;
DataPtr = static_cast<T*>(addr) + ShMutexSize;
}
template<typename T>
SWMRSharedMemArray<T>::SWMRSharedMemArray(std::string Name) :
ShMutexSize(sizeof(interprocess_sharable_mutex)),
isManager(false), Name(Name)
{
shm = new shared_memory_object(open_only, Name.c_str(), read_write);
region = new mapped_region(*shm, read_write);
_length = (region->get_size() - ShMutexSize) / sizeof(T);
void *addr = region->get_address();
mtx = static_cast<decltype(mtx)>(addr);
DataPtr = static_cast<T*>(addr) + ShMutexSize;
}
在主机端,一切看起来都还不错。
但是在为 Clients 构建时存在问题:
当我比较第一个和第二个实例的 shm 和区域对象时
(具有不同的名称 ofc,但相同的长度和模板类型)
我看到很多应该有所不同的成员却没有。
地址和成员 m_filename 果然不一样,但是成员 m_handle 是一样的。
对于区域,两个地址不同,但所有成员都是相同的。
我希望有人知道发生了什么事。
此致
乌作
我没有完全理解您的代码,但我对手动内存管理的古老使用感到震惊。每当我在 C++ 中看到 "sizeof()" 时,我都会有点担心:)
由于缺乏抽象,混淆几乎是不可避免的,编译器也无能为力,因为你在 "Leave Me Alone - I Know What I'm Doing" 土地上。
具体来说,这看起来是错误的:
DataPtr = static_cast<T *>(addr) + ShMutexSize;
当 sizeof(T)==sizeof(char)
(IOW,T
是一个字节)时,这可能是正确的,但否则你会得到 指针算法,这意味着你添加 sizeof(T)
ShMutexSize
次。这肯定是错误的,因为你只为互斥量+元素数据的大小预留空间,直接相邻。
因此,由于索引超出了共享内存区域的大小,您将无法使用 space 和 Undefined Behavior。
所以,让我对比两个样本;
- 减少对指针运算的依赖
- 通过使用托管共享内存段消除所有手动内存管理
1。手动
不太需要相同数量指针的手动方法 trickery/resource 管理可能如下所示:
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <boost/interprocess/sync/interprocess_sharable_mutex.hpp>
#include <boost/thread/lock_guard.hpp>
namespace bip = boost::interprocess;
namespace SWMR {
static struct server_mode_t {} const/*expr*/ server_mode = server_mode_t();
static struct client_mode_t {} const/*expr*/ client_mode = client_mode_t();
typedef bip::interprocess_sharable_mutex mutex;
typedef boost::lock_guard<mutex> guard;
template <typename T, size_t N> struct SharedMemArray {
SharedMemArray(server_mode_t, std::string const& name)
: isManager(true), _name(name),
_shm(do_create(_name.c_str())),
_region(_shm, bip::read_write)
{
_data = new (_region.get_address()) data_t;
}
SharedMemArray(client_mode_t, std::string const& name)
: isManager(false), _name(name),
_shm(do_open(_name.c_str())),
_region(_shm, bip::read_write),
_data(static_cast<data_t*>(_region.get_address()))
{
assert(sizeof(data_t) == _region.get_size());
}
private:
typedef bip::shared_memory_object shm_t;
struct data_t {
mutable mutex mtx;
T DataPtr[N];
};
bool isManager;
const std::string _name;
shm_t _shm;
bip::mapped_region _region;
data_t *_data;
// functions to manage the shared memory
shm_t static do_create(char const* name) {
shm_t::remove(name);
shm_t result(bip::create_only, name, bip::read_write);
result.truncate(sizeof(data_t));
return boost::move(result);
}
shm_t static do_open(char const* name) {
return shm_t(bip::open_only, name, bip::read_write);
}
public:
mutex& get_mutex() const { return _data->mtx; }
typedef T *iterator;
typedef T const *const_iterator;
iterator data() { return _data->DataPtr; }
const_iterator data() const { return _data->DataPtr; }
iterator begin() { return data(); }
const_iterator begin() const { return data(); }
iterator end() { return begin() + N; }
const_iterator end() const { return begin() + N; }
const_iterator cbegin() const { return begin(); }
const_iterator cend() const { return end(); }
};
}
#include <vector>
static const std::string APP_UUID = "61ab4f43-2d68-46e1-9c8d-31d577ce3aa7";
struct UserData {
int i;
float f;
};
#include <boost/range/algorithm.hpp>
#include <boost/foreach.hpp>
#include <iostream>
int main() {
using namespace SWMR;
SharedMemArray<int, 20> s_ints (server_mode, APP_UUID + "-ints");
SharedMemArray<float, 72> s_floats (server_mode, APP_UUID + "-floats");
SharedMemArray<UserData, 10> s_udts (server_mode, APP_UUID + "-udts");
{
guard lk(s_ints.get_mutex());
boost::fill(s_ints, 42);
}
{
guard lk(s_floats.get_mutex());
boost::fill(s_floats, 31415);
}
{
guard lk(s_udts.get_mutex());
UserData udt = { 42, 3.14 };
boost::fill(s_udts, udt);
}
SharedMemArray<int, 20> c_ints (client_mode, APP_UUID + "-ints");
SharedMemArray<float, 72> c_floats (client_mode, APP_UUID + "-floats");
SharedMemArray<UserData, 10> c_udts (client_mode, APP_UUID + "-udts");
{
guard lk(c_ints.get_mutex());
assert(boost::equal(std::vector<int>(boost::size(c_ints), 42), c_ints));
}
{
guard lk(c_floats.get_mutex());
assert(boost::equal(std::vector<int>(boost::size(c_floats), 31415), c_floats));
}
{
guard lk(c_udts.get_mutex());
BOOST_FOREACH(UserData& udt, c_udts)
std::cout << udt.i << "\t" << udt.f << "\n";
}
}
备注
- 它重用了代码
- 它不会进行不必要的动态分配(这使得 class 更容易 "get right" 三规则)
- 它使用
data_t
结构来摆脱手动偏移计算(您可以只做 data->mtx
或 data->DataPtr
)
它添加了 iterator
和 begin()
/end()
定义,以便您可以直接使用 SharedMemArray
作为范围,例如使用 boost::equal
和 BOOST_FOREACH
:
等算法
assert(boost::equal(some_vector, c_floats));
BOOST_FOREACH(UserData& udt, c_udts)
std::cout << udt.i << "\t" << udt.f << "\n";
目前,它使用静态已知数量的元素 (N
)。
如果您不想要这个,我肯定选择使用托管段(小于2)的方法。因为这将为您处理所有(重新)分配机制。
2。使用 managed_shared_memory
段
当我们需要动态大小的数组时,我们在 C++ 中使用什么? 正确: std::vector
.
现在可以教 std::vector
从共享内存分配,但您需要向它传递一个 Boost Interprocess allocator
。这个分配器知道如何使用 segment_manager
从共享内存执行分配。
这是使用 managed_shared_memory
的相对直接的翻译
#include <boost/container/scoped_allocator.hpp>
#include <boost/container/vector.hpp>
#include <boost/container/string.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/offset_ptr.hpp>
#include <boost/interprocess/sync/interprocess_sharable_mutex.hpp>
#include <boost/thread/lock_guard.hpp>
namespace Shared {
namespace bip = boost::interprocess;
namespace bc = boost::container;
using shm_t = bip::managed_shared_memory;
using mutex = bip::interprocess_sharable_mutex;
using guard = boost::lock_guard<mutex>;
template <typename T> using allocator = bc::scoped_allocator_adaptor<
bip::allocator<T, shm_t::segment_manager>
>;
template <typename T> using vector = bc::vector<T, allocator<T> >;
template <typename T> using basic_string = bc::basic_string<T, std::char_traits<T>, allocator<T> >;
using string = basic_string<char>;
using wstring = basic_string<wchar_t>;
}
namespace SWMR {
namespace bip = boost::interprocess;
static struct server_mode_t {} const/*expr*/ server_mode = server_mode_t();
static struct client_mode_t {} const/*expr*/ client_mode = client_mode_t();
template <typename T> struct SharedMemArray {
private:
struct data_t {
using allocator_type = Shared::allocator<void>;
data_t(size_t N, allocator_type alloc) : elements(alloc) { elements.resize(N); }
data_t(allocator_type alloc) : elements(alloc) {}
mutable Shared::mutex mtx;
Shared::vector<T> elements;
};
bool isManager;
const std::string _name;
Shared::shm_t _shm;
data_t *_data;
// functions to manage the shared memory
Shared::shm_t static do_create(char const* name) {
bip::shared_memory_object::remove(name);
Shared::shm_t result(bip::create_only, name, 1ul << 20); // ~1 MiB
return boost::move(result);
}
Shared::shm_t static do_open(char const* name) {
return Shared::shm_t(bip::open_only, name);
}
public:
SharedMemArray(server_mode_t, std::string const& name, size_t N = 0)
: isManager(true), _name(name), _shm(do_create(_name.c_str()))
{
_data = _shm.find_or_construct<data_t>(name.c_str())(N, _shm.get_segment_manager());
}
SharedMemArray(client_mode_t, std::string const& name)
: isManager(false), _name(name), _shm(do_open(_name.c_str()))
{
auto found = _shm.find<data_t>(name.c_str());
assert(found.second);
_data = found.first;
}
Shared::mutex& mutex() const { return _data->mtx; }
Shared::vector<T> & elements() { return _data->elements; }
Shared::vector<T> const& elements() const { return _data->elements; }
};
}
#include <vector>
static const std::string APP_UUID = "93f6b721-1d34-46d9-9877-f967fea61cf2";
struct UserData {
using allocator_type = Shared::allocator<void>;
UserData(allocator_type alloc) : text(alloc) {}
UserData(UserData const& other, allocator_type alloc) : i(other.i), text(other.text, alloc) {}
UserData(int i, Shared::string t) : i(i), text(t) {}
template <typename T> UserData(int i, T&& t, allocator_type alloc) : i(i), text(std::forward<T>(t), alloc) {}
// data
int i;
Shared::string text;
};
#include <boost/range/algorithm.hpp>
#include <boost/foreach.hpp>
#include <iostream>
int main() {
using namespace SWMR;
SharedMemArray<int> s_ints(server_mode, APP_UUID + "-ints", 20);
SharedMemArray<UserData> s_udts(server_mode, APP_UUID + "-udts");
// server code
{
Shared::guard lk(s_ints.mutex());
boost::fill(s_ints.elements(), 99);
// or manipulate the vector. Any allocations go to the shared memory segment automatically
s_ints.elements().push_back(42);
s_ints.elements().assign(20, 42);
}
{
Shared::guard lk(s_udts.mutex());
s_udts.elements().emplace_back(1, "one");
}
// client code
SharedMemArray<int> c_ints(client_mode, APP_UUID + "-ints");
SharedMemArray<UserData> c_udts(client_mode, APP_UUID + "-udts");
{
Shared::guard lk(c_ints.mutex());
auto& e = c_ints.elements();
assert(boost::equal(std::vector<int>(20, 42), e));
}
{
Shared::guard lk(c_udts.mutex());
BOOST_FOREACH(UserData& udt, c_udts.elements())
std::cout << udt.i << "\t'" << udt.text << "'\n";
}
}
备注:
由于您现在首先存储 class C++ 对象,因此大小不是静态的。事实上,您可以 push_back
如果超出容量,容器将使用段的分配器重新分配。
为了 namespace Shared
中的便利类型定义,我选择使用 C++11。然而,所有这些都可以在 c++03 中工作,尽管更冗长
我也一直选择使用 范围分配器。这意味着如果 T
是一个(用户定义的)类型/也/使用分配器(例如 all standard containers, std::deque
, std::packaged_task
, std::tuple
etc. 分配器的段引用将在内部构造时隐式传递给元素。这例如为什么行
elements.resize(N);
和
s_udts.elements().emplace_back(1, "one");
无需为元素的构造函数显式传递分配器即可进行编译。
示例 UserData
class 利用这个来展示如何包含一个 std::string
(或者实际上,一个 Shared::string
),其中 神奇地 从与容器相同的内存段分配。
3。奖金
另请注意,这开启了将所有容器存储在一个容器中的可能性 shared_memory_object
这可能是有益的,因此我提出了一种显示此方法的变体:
#include <boost/container/scoped_allocator.hpp>
#include <boost/container/vector.hpp>
#include <boost/container/string.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/offset_ptr.hpp>
#include <boost/interprocess/sync/interprocess_sharable_mutex.hpp>
#include <boost/thread/lock_guard.hpp>
namespace Shared {
namespace bip = boost::interprocess;
namespace bc = boost::container;
using msm_t = bip::managed_shared_memory;
using mutex = bip::interprocess_sharable_mutex;
using guard = boost::lock_guard<mutex>;
template <typename T> using allocator = bc::scoped_allocator_adaptor<
bip::allocator<T, msm_t::segment_manager>
>;
template <typename T> using vector = bc::vector<T, allocator<T> >;
template <typename T> using basic_string = bc::basic_string<T, std::char_traits<T>, allocator<T> >;
using string = basic_string<char>;
using wstring = basic_string<wchar_t>;
}
namespace SWMR {
namespace bip = boost::interprocess;
namespace bc = boost::container;
class Segment {
public:
// LockableObject, base template
//
// LockableObject contains a `Shared::mutex` and an object of type T
template <typename T, typename Enable = void> struct LockableObject;
// Partial specialization for the case when the wrapped object cannot
// use the shared allocator: the constructor is just forwarded
template <typename T>
struct LockableObject<T, typename boost::disable_if<bc::uses_allocator<T, Shared::allocator<T> >, void>::type>
{
template <typename... CtorArgs>
LockableObject(CtorArgs&&... args) : object(std::forward<CtorArgs>(args)...) {}
LockableObject() : object() {}
mutable Shared::mutex mutex;
T object;
private:
friend class Segment;
template <typename... CtorArgs>
static LockableObject& locate_by_name(Shared::msm_t& msm, const char* tag, CtorArgs&&... args) {
return *msm.find_or_construct<LockableObject<T> >(tag)(std::forward<CtorArgs>(args)...);
}
};
// Partial specialization for the case where the contained object can
// use the shared allocator;
//
// Construction (using locate_by_name) adds the allocator as the last
// argument.
template <typename T>
struct LockableObject<T, typename boost::enable_if<bc::uses_allocator<T, Shared::allocator<T> >, void>::type>
{
using allocator_type = Shared::allocator<void>;
template <typename... CtorArgs>
LockableObject(CtorArgs&&... args) : object(std::forward<CtorArgs>(args)...) {}
LockableObject(allocator_type alloc = {}) : object(alloc) {}
mutable Shared::mutex mutex;
T object;
private:
friend class Segment;
template <typename... CtorArgs>
static LockableObject& locate_by_name(Shared::msm_t& msm, const char* tag, CtorArgs&&... args) {
return *msm.find_or_construct<LockableObject>(tag)(std::forward<CtorArgs>(args)..., Shared::allocator<T>(msm.get_segment_manager()));
}
};
Segment(std::string const& name, size_t capacity = 1024*1024) // default 1 MiB
: _msm(bip::open_or_create, name.c_str(), capacity)
{
}
template <typename T, typename... CtorArgs>
LockableObject<T>& getLockable(char const* tag, CtorArgs&&... args) {
return LockableObject<T>::locate_by_name(_msm, tag, std::forward<CtorArgs>(args)...);
}
private:
Shared::msm_t _msm;
};
}
#include <vector>
static char const* const APP_UUID = "249f3878-3ddf-4473-84b2-755998952da1";
struct UserData {
using allocator_type = Shared::allocator<void>;
using String = Shared::string;
UserData(allocator_type alloc) : text(alloc) { }
UserData(int i, String t) : i(i), text(t) { }
UserData(UserData const& other, allocator_type alloc) : i(other.i), text(other.text, alloc) { }
template <typename T>
UserData(int i, T&& t, allocator_type alloc)
: i(i), text(std::forward<T>(t), alloc)
{ }
// data
int i;
String text;
};
#include <boost/range/algorithm.hpp>
#include <boost/foreach.hpp>
#include <iostream>
int main() {
using IntVec = Shared::vector<int>;
using UdtVec = Shared::vector<UserData>;
boost::interprocess::shared_memory_object::remove(APP_UUID); // for demo
// server code
{
SWMR::Segment server(APP_UUID);
auto& s_ints = server.getLockable<IntVec>("ints", std::initializer_list<int> {1,2,3,4,5,6,7,42}); // allocator automatically added
auto& s_udts = server.getLockable<UdtVec>("udts");
{
Shared::guard lk(s_ints.mutex);
boost::fill(s_ints.object, 99);
// or manipulate the vector. Any allocations go to the shared memory segment automatically
s_ints.object.push_back(42);
s_ints.object.assign(20, 42);
}
{
Shared::guard lk(s_udts.mutex);
s_udts.object.emplace_back(1, "one"); // allocates the string in shared memory, and the UserData element too
}
}
// client code
{
SWMR::Segment client(APP_UUID);
auto& c_ints = client.getLockable<IntVec>("ints", 20, 999); // the ctor arguments are ignored here
auto& c_udts = client.getLockable<UdtVec>("udts");
{
Shared::guard lk(c_ints.mutex);
IntVec& ivec = c_ints.object;
assert(boost::equal(std::vector<int>(20, 42), ivec));
}
{
Shared::guard lk(c_udts.mutex);
BOOST_FOREACH(UserData& udt, c_udts.object)
std::cout << udt.i << "\t'" << udt.text << "'\n";
}
}
}
备注:
您现在可以存储任何东西,而不仅仅是 "dynamic arrays" (vector<T>
)。你可以这样做:
auto& c_udts = client.getLockable<double>("a_single_double");
当您存储与共享分配器兼容的容器时,LockableObject
的构造方法将透明地将分配器实例添加为包含的 [=50= 的最后一个构造函数参数].
我把remove()
调用移出了Segment
class,这样就不用区分client/server模式了。我们只使用 open_or_create
和 find_or_construct
.
为了在录制程序和显示程序(不能相同)之间传输视频流,我使用共享内存。 为了同步访问,我将 class 放在一起,其中包含 shared_memory_object、mapped_region 和 interprocess_sharable_mutex(所有 boost::interprocess)
我写了 2 个构造函数,一个用于 "Host" 端,一个用于 "Client" 端。 当我使用我的 class 传输一个视频流时,它工作得很好。 但是当我尝试传输两个视频流时出现了一些问题。
首先:这里是构造函数代码: (第一个是Host-Constructor,第二个是Client)
template<typename T>
SWMRSharedMemArray<T>::SWMRSharedMemArray(std::string Name, size_t length):
ShMutexSize(sizeof(interprocess_sharable_mutex)),
isManager(true), _length(length), Name(Name)
{
shared_memory_object::remove(Name.c_str());
shm = new shared_memory_object(create_only, Name.c_str(), read_write);
shm->truncate(ShMutexSize + sizeof(T)*length);
region = new mapped_region(*shm, read_write);
void *addr = region->get_address();
mtx = new(addr) interprocess_sharable_mutex;
DataPtr = static_cast<T*>(addr) + ShMutexSize;
}
template<typename T>
SWMRSharedMemArray<T>::SWMRSharedMemArray(std::string Name) :
ShMutexSize(sizeof(interprocess_sharable_mutex)),
isManager(false), Name(Name)
{
shm = new shared_memory_object(open_only, Name.c_str(), read_write);
region = new mapped_region(*shm, read_write);
_length = (region->get_size() - ShMutexSize) / sizeof(T);
void *addr = region->get_address();
mtx = static_cast<decltype(mtx)>(addr);
DataPtr = static_cast<T*>(addr) + ShMutexSize;
}
在主机端,一切看起来都还不错。 但是在为 Clients 构建时存在问题: 当我比较第一个和第二个实例的 shm 和区域对象时 (具有不同的名称 ofc,但相同的长度和模板类型) 我看到很多应该有所不同的成员却没有。 地址和成员 m_filename 果然不一样,但是成员 m_handle 是一样的。 对于区域,两个地址不同,但所有成员都是相同的。
我希望有人知道发生了什么事。 此致 乌作
我没有完全理解您的代码,但我对手动内存管理的古老使用感到震惊。每当我在 C++ 中看到 "sizeof()" 时,我都会有点担心:)
由于缺乏抽象,混淆几乎是不可避免的,编译器也无能为力,因为你在 "Leave Me Alone - I Know What I'm Doing" 土地上。
具体来说,这看起来是错误的:
DataPtr = static_cast<T *>(addr) + ShMutexSize;
当 sizeof(T)==sizeof(char)
(IOW,T
是一个字节)时,这可能是正确的,但否则你会得到 指针算法,这意味着你添加 sizeof(T)
ShMutexSize
次。这肯定是错误的,因为你只为互斥量+元素数据的大小预留空间,直接相邻。
因此,由于索引超出了共享内存区域的大小,您将无法使用 space 和 Undefined Behavior。
所以,让我对比两个样本;
- 减少对指针运算的依赖
- 通过使用托管共享内存段消除所有手动内存管理
1。手动
不太需要相同数量指针的手动方法 trickery/resource 管理可能如下所示:
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <boost/interprocess/sync/interprocess_sharable_mutex.hpp>
#include <boost/thread/lock_guard.hpp>
namespace bip = boost::interprocess;
namespace SWMR {
static struct server_mode_t {} const/*expr*/ server_mode = server_mode_t();
static struct client_mode_t {} const/*expr*/ client_mode = client_mode_t();
typedef bip::interprocess_sharable_mutex mutex;
typedef boost::lock_guard<mutex> guard;
template <typename T, size_t N> struct SharedMemArray {
SharedMemArray(server_mode_t, std::string const& name)
: isManager(true), _name(name),
_shm(do_create(_name.c_str())),
_region(_shm, bip::read_write)
{
_data = new (_region.get_address()) data_t;
}
SharedMemArray(client_mode_t, std::string const& name)
: isManager(false), _name(name),
_shm(do_open(_name.c_str())),
_region(_shm, bip::read_write),
_data(static_cast<data_t*>(_region.get_address()))
{
assert(sizeof(data_t) == _region.get_size());
}
private:
typedef bip::shared_memory_object shm_t;
struct data_t {
mutable mutex mtx;
T DataPtr[N];
};
bool isManager;
const std::string _name;
shm_t _shm;
bip::mapped_region _region;
data_t *_data;
// functions to manage the shared memory
shm_t static do_create(char const* name) {
shm_t::remove(name);
shm_t result(bip::create_only, name, bip::read_write);
result.truncate(sizeof(data_t));
return boost::move(result);
}
shm_t static do_open(char const* name) {
return shm_t(bip::open_only, name, bip::read_write);
}
public:
mutex& get_mutex() const { return _data->mtx; }
typedef T *iterator;
typedef T const *const_iterator;
iterator data() { return _data->DataPtr; }
const_iterator data() const { return _data->DataPtr; }
iterator begin() { return data(); }
const_iterator begin() const { return data(); }
iterator end() { return begin() + N; }
const_iterator end() const { return begin() + N; }
const_iterator cbegin() const { return begin(); }
const_iterator cend() const { return end(); }
};
}
#include <vector>
static const std::string APP_UUID = "61ab4f43-2d68-46e1-9c8d-31d577ce3aa7";
struct UserData {
int i;
float f;
};
#include <boost/range/algorithm.hpp>
#include <boost/foreach.hpp>
#include <iostream>
int main() {
using namespace SWMR;
SharedMemArray<int, 20> s_ints (server_mode, APP_UUID + "-ints");
SharedMemArray<float, 72> s_floats (server_mode, APP_UUID + "-floats");
SharedMemArray<UserData, 10> s_udts (server_mode, APP_UUID + "-udts");
{
guard lk(s_ints.get_mutex());
boost::fill(s_ints, 42);
}
{
guard lk(s_floats.get_mutex());
boost::fill(s_floats, 31415);
}
{
guard lk(s_udts.get_mutex());
UserData udt = { 42, 3.14 };
boost::fill(s_udts, udt);
}
SharedMemArray<int, 20> c_ints (client_mode, APP_UUID + "-ints");
SharedMemArray<float, 72> c_floats (client_mode, APP_UUID + "-floats");
SharedMemArray<UserData, 10> c_udts (client_mode, APP_UUID + "-udts");
{
guard lk(c_ints.get_mutex());
assert(boost::equal(std::vector<int>(boost::size(c_ints), 42), c_ints));
}
{
guard lk(c_floats.get_mutex());
assert(boost::equal(std::vector<int>(boost::size(c_floats), 31415), c_floats));
}
{
guard lk(c_udts.get_mutex());
BOOST_FOREACH(UserData& udt, c_udts)
std::cout << udt.i << "\t" << udt.f << "\n";
}
}
备注
- 它重用了代码
- 它不会进行不必要的动态分配(这使得 class 更容易 "get right" 三规则)
- 它使用
data_t
结构来摆脱手动偏移计算(您可以只做data->mtx
或data->DataPtr
) 它添加了
等算法iterator
和begin()
/end()
定义,以便您可以直接使用SharedMemArray
作为范围,例如使用boost::equal
和BOOST_FOREACH
:assert(boost::equal(some_vector, c_floats)); BOOST_FOREACH(UserData& udt, c_udts) std::cout << udt.i << "\t" << udt.f << "\n";
目前,它使用静态已知数量的元素 (
N
)。
如果您不想要这个,我肯定选择使用托管段(小于2)的方法。因为这将为您处理所有(重新)分配机制。
2。使用 managed_shared_memory
段
当我们需要动态大小的数组时,我们在 C++ 中使用什么? 正确: std::vector
.
现在可以教 std::vector
从共享内存分配,但您需要向它传递一个 Boost Interprocess allocator
。这个分配器知道如何使用 segment_manager
从共享内存执行分配。
这是使用 managed_shared_memory
#include <boost/container/scoped_allocator.hpp>
#include <boost/container/vector.hpp>
#include <boost/container/string.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/offset_ptr.hpp>
#include <boost/interprocess/sync/interprocess_sharable_mutex.hpp>
#include <boost/thread/lock_guard.hpp>
namespace Shared {
namespace bip = boost::interprocess;
namespace bc = boost::container;
using shm_t = bip::managed_shared_memory;
using mutex = bip::interprocess_sharable_mutex;
using guard = boost::lock_guard<mutex>;
template <typename T> using allocator = bc::scoped_allocator_adaptor<
bip::allocator<T, shm_t::segment_manager>
>;
template <typename T> using vector = bc::vector<T, allocator<T> >;
template <typename T> using basic_string = bc::basic_string<T, std::char_traits<T>, allocator<T> >;
using string = basic_string<char>;
using wstring = basic_string<wchar_t>;
}
namespace SWMR {
namespace bip = boost::interprocess;
static struct server_mode_t {} const/*expr*/ server_mode = server_mode_t();
static struct client_mode_t {} const/*expr*/ client_mode = client_mode_t();
template <typename T> struct SharedMemArray {
private:
struct data_t {
using allocator_type = Shared::allocator<void>;
data_t(size_t N, allocator_type alloc) : elements(alloc) { elements.resize(N); }
data_t(allocator_type alloc) : elements(alloc) {}
mutable Shared::mutex mtx;
Shared::vector<T> elements;
};
bool isManager;
const std::string _name;
Shared::shm_t _shm;
data_t *_data;
// functions to manage the shared memory
Shared::shm_t static do_create(char const* name) {
bip::shared_memory_object::remove(name);
Shared::shm_t result(bip::create_only, name, 1ul << 20); // ~1 MiB
return boost::move(result);
}
Shared::shm_t static do_open(char const* name) {
return Shared::shm_t(bip::open_only, name);
}
public:
SharedMemArray(server_mode_t, std::string const& name, size_t N = 0)
: isManager(true), _name(name), _shm(do_create(_name.c_str()))
{
_data = _shm.find_or_construct<data_t>(name.c_str())(N, _shm.get_segment_manager());
}
SharedMemArray(client_mode_t, std::string const& name)
: isManager(false), _name(name), _shm(do_open(_name.c_str()))
{
auto found = _shm.find<data_t>(name.c_str());
assert(found.second);
_data = found.first;
}
Shared::mutex& mutex() const { return _data->mtx; }
Shared::vector<T> & elements() { return _data->elements; }
Shared::vector<T> const& elements() const { return _data->elements; }
};
}
#include <vector>
static const std::string APP_UUID = "93f6b721-1d34-46d9-9877-f967fea61cf2";
struct UserData {
using allocator_type = Shared::allocator<void>;
UserData(allocator_type alloc) : text(alloc) {}
UserData(UserData const& other, allocator_type alloc) : i(other.i), text(other.text, alloc) {}
UserData(int i, Shared::string t) : i(i), text(t) {}
template <typename T> UserData(int i, T&& t, allocator_type alloc) : i(i), text(std::forward<T>(t), alloc) {}
// data
int i;
Shared::string text;
};
#include <boost/range/algorithm.hpp>
#include <boost/foreach.hpp>
#include <iostream>
int main() {
using namespace SWMR;
SharedMemArray<int> s_ints(server_mode, APP_UUID + "-ints", 20);
SharedMemArray<UserData> s_udts(server_mode, APP_UUID + "-udts");
// server code
{
Shared::guard lk(s_ints.mutex());
boost::fill(s_ints.elements(), 99);
// or manipulate the vector. Any allocations go to the shared memory segment automatically
s_ints.elements().push_back(42);
s_ints.elements().assign(20, 42);
}
{
Shared::guard lk(s_udts.mutex());
s_udts.elements().emplace_back(1, "one");
}
// client code
SharedMemArray<int> c_ints(client_mode, APP_UUID + "-ints");
SharedMemArray<UserData> c_udts(client_mode, APP_UUID + "-udts");
{
Shared::guard lk(c_ints.mutex());
auto& e = c_ints.elements();
assert(boost::equal(std::vector<int>(20, 42), e));
}
{
Shared::guard lk(c_udts.mutex());
BOOST_FOREACH(UserData& udt, c_udts.elements())
std::cout << udt.i << "\t'" << udt.text << "'\n";
}
}
备注:
由于您现在首先存储 class C++ 对象,因此大小不是静态的。事实上,您可以
push_back
如果超出容量,容器将使用段的分配器重新分配。为了
namespace Shared
中的便利类型定义,我选择使用 C++11。然而,所有这些都可以在 c++03 中工作,尽管更冗长我也一直选择使用 范围分配器。这意味着如果
T
是一个(用户定义的)类型/也/使用分配器(例如 all standard containers,std::deque
,std::packaged_task
,std::tuple
etc. 分配器的段引用将在内部构造时隐式传递给元素。这例如为什么行elements.resize(N);
和
s_udts.elements().emplace_back(1, "one");
无需为元素的构造函数显式传递分配器即可进行编译。
示例
UserData
class 利用这个来展示如何包含一个std::string
(或者实际上,一个Shared::string
),其中 神奇地 从与容器相同的内存段分配。
3。奖金
另请注意,这开启了将所有容器存储在一个容器中的可能性 shared_memory_object
这可能是有益的,因此我提出了一种显示此方法的变体:
#include <boost/container/scoped_allocator.hpp>
#include <boost/container/vector.hpp>
#include <boost/container/string.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/offset_ptr.hpp>
#include <boost/interprocess/sync/interprocess_sharable_mutex.hpp>
#include <boost/thread/lock_guard.hpp>
namespace Shared {
namespace bip = boost::interprocess;
namespace bc = boost::container;
using msm_t = bip::managed_shared_memory;
using mutex = bip::interprocess_sharable_mutex;
using guard = boost::lock_guard<mutex>;
template <typename T> using allocator = bc::scoped_allocator_adaptor<
bip::allocator<T, msm_t::segment_manager>
>;
template <typename T> using vector = bc::vector<T, allocator<T> >;
template <typename T> using basic_string = bc::basic_string<T, std::char_traits<T>, allocator<T> >;
using string = basic_string<char>;
using wstring = basic_string<wchar_t>;
}
namespace SWMR {
namespace bip = boost::interprocess;
namespace bc = boost::container;
class Segment {
public:
// LockableObject, base template
//
// LockableObject contains a `Shared::mutex` and an object of type T
template <typename T, typename Enable = void> struct LockableObject;
// Partial specialization for the case when the wrapped object cannot
// use the shared allocator: the constructor is just forwarded
template <typename T>
struct LockableObject<T, typename boost::disable_if<bc::uses_allocator<T, Shared::allocator<T> >, void>::type>
{
template <typename... CtorArgs>
LockableObject(CtorArgs&&... args) : object(std::forward<CtorArgs>(args)...) {}
LockableObject() : object() {}
mutable Shared::mutex mutex;
T object;
private:
friend class Segment;
template <typename... CtorArgs>
static LockableObject& locate_by_name(Shared::msm_t& msm, const char* tag, CtorArgs&&... args) {
return *msm.find_or_construct<LockableObject<T> >(tag)(std::forward<CtorArgs>(args)...);
}
};
// Partial specialization for the case where the contained object can
// use the shared allocator;
//
// Construction (using locate_by_name) adds the allocator as the last
// argument.
template <typename T>
struct LockableObject<T, typename boost::enable_if<bc::uses_allocator<T, Shared::allocator<T> >, void>::type>
{
using allocator_type = Shared::allocator<void>;
template <typename... CtorArgs>
LockableObject(CtorArgs&&... args) : object(std::forward<CtorArgs>(args)...) {}
LockableObject(allocator_type alloc = {}) : object(alloc) {}
mutable Shared::mutex mutex;
T object;
private:
friend class Segment;
template <typename... CtorArgs>
static LockableObject& locate_by_name(Shared::msm_t& msm, const char* tag, CtorArgs&&... args) {
return *msm.find_or_construct<LockableObject>(tag)(std::forward<CtorArgs>(args)..., Shared::allocator<T>(msm.get_segment_manager()));
}
};
Segment(std::string const& name, size_t capacity = 1024*1024) // default 1 MiB
: _msm(bip::open_or_create, name.c_str(), capacity)
{
}
template <typename T, typename... CtorArgs>
LockableObject<T>& getLockable(char const* tag, CtorArgs&&... args) {
return LockableObject<T>::locate_by_name(_msm, tag, std::forward<CtorArgs>(args)...);
}
private:
Shared::msm_t _msm;
};
}
#include <vector>
static char const* const APP_UUID = "249f3878-3ddf-4473-84b2-755998952da1";
struct UserData {
using allocator_type = Shared::allocator<void>;
using String = Shared::string;
UserData(allocator_type alloc) : text(alloc) { }
UserData(int i, String t) : i(i), text(t) { }
UserData(UserData const& other, allocator_type alloc) : i(other.i), text(other.text, alloc) { }
template <typename T>
UserData(int i, T&& t, allocator_type alloc)
: i(i), text(std::forward<T>(t), alloc)
{ }
// data
int i;
String text;
};
#include <boost/range/algorithm.hpp>
#include <boost/foreach.hpp>
#include <iostream>
int main() {
using IntVec = Shared::vector<int>;
using UdtVec = Shared::vector<UserData>;
boost::interprocess::shared_memory_object::remove(APP_UUID); // for demo
// server code
{
SWMR::Segment server(APP_UUID);
auto& s_ints = server.getLockable<IntVec>("ints", std::initializer_list<int> {1,2,3,4,5,6,7,42}); // allocator automatically added
auto& s_udts = server.getLockable<UdtVec>("udts");
{
Shared::guard lk(s_ints.mutex);
boost::fill(s_ints.object, 99);
// or manipulate the vector. Any allocations go to the shared memory segment automatically
s_ints.object.push_back(42);
s_ints.object.assign(20, 42);
}
{
Shared::guard lk(s_udts.mutex);
s_udts.object.emplace_back(1, "one"); // allocates the string in shared memory, and the UserData element too
}
}
// client code
{
SWMR::Segment client(APP_UUID);
auto& c_ints = client.getLockable<IntVec>("ints", 20, 999); // the ctor arguments are ignored here
auto& c_udts = client.getLockable<UdtVec>("udts");
{
Shared::guard lk(c_ints.mutex);
IntVec& ivec = c_ints.object;
assert(boost::equal(std::vector<int>(20, 42), ivec));
}
{
Shared::guard lk(c_udts.mutex);
BOOST_FOREACH(UserData& udt, c_udts.object)
std::cout << udt.i << "\t'" << udt.text << "'\n";
}
}
}
备注:
您现在可以存储任何东西,而不仅仅是 "dynamic arrays" (
vector<T>
)。你可以这样做:auto& c_udts = client.getLockable<double>("a_single_double");
当您存储与共享分配器兼容的容器时,
LockableObject
的构造方法将透明地将分配器实例添加为包含的 [=50= 的最后一个构造函数参数].我把
remove()
调用移出了Segment
class,这样就不用区分client/server模式了。我们只使用open_or_create
和find_or_construct
.