访问共享内存时读取访问冲突
Read access violation when accessing shared memory
我根据 boost 示例在共享内存中实现了一个线程安全队列-class。对于一个简单的 producer/consumer 模型,它按预期工作。
作为下一步,我定义了由 ConcurrentIPCQueue
class 实现的接口 IConcurrentIPCQueue
。我需要这个接口,这样我就可以为另一个问题实现一个队列适配器。
我的第一个版本和下面的当前版本之间的唯一区别如下:
第一个版本:
template <class T> class ConcurrentIPCQueue
现在添加信息,我想实现这样的接口:
当前版本:
`template <class T> class ConcurrentIPCQueue :public IConcurrentIPCQueue<T>`
导致消费者端读取访问冲突。在生产者方面,我可以轻松地自己正确地 push_back
和 pop_front
数据。但奇怪的是,在消费者方面,我无法访问共享内存(尽管来自 segment.find returns 的对正确地是一个地址和 1)。
所以问题是,为什么实现接口的版本会在消费者方面产生影响并导致这个奇怪的错误。我该如何解决?
为了使示例简短,我在这里展示了一个极简的队列模型:
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/containers/deque.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/sync/interprocess_condition.hpp>
#include <boost/interprocess/sync/interprocess_mutex.hpp>
#include <boost/thread/lock_guard.hpp>
#include <sstream>
namespace boost_ipc = boost::interprocess;
static char const *SHMEMNAME= "SHMEM";
static char const *SHQUEUENAME= "MYQUEUE";
template <class T> class IConcurrentIPCQueue
{
public:
virtual void push_back(T const & data) = 0;
virtual bool pop_front(T & data) = 0;
virtual unsigned int size() = 0;
};
template <class T> class ConcurrentIPCQueue :public IConcurrentIPCQueue<T>
{
public:
// allocator for allocating memory from the shared memory
typedef boost_ipc::allocator<T, boost_ipc::managed_shared_memory::segment_manager> ShmemAlloc;
typedef boost_ipc::interprocess_mutex IPC_Mutex;
typedef boost_ipc::interprocess_condition IPC_Cond;
typedef boost::lock_guard<IPC_Mutex> LockGuard;
ConcurrentIPCQueue(ShmemAlloc salloc) : mQueue_(salloc) { }
void push_back(T const & data)
{
{
LockGuard lock(mMutex_);
mQueue_.push_back(data);
}
mWait_.notify_one();
}
bool pop_front(T & data)
{
LockGuard lock(mMutex_);
if (mQueue_.empty())
return false;
data = mQueue_.front(); // return reference to first element
mQueue_.pop_front(); // remove the first element
return true;
}
unsigned int size()
{
LockGuard lock(mMutex_);
return mQueue_.size();
}
private:
boost_ipc::deque<T, ShmemAlloc> mQueue_;
IPC_Mutex mMutex_;
IPC_Cond mWait_;
};
typedef ConcurrentIPCQueue<char> myqueue;
void consumer()
{
boost_ipc::managed_shared_memory openedSegment(boost_ipc::open_only, SHMEMNAME);
myqueue*openedQueue = openedSegment.find<myqueue>(SHQUEUENAME).first;
char tmp;
while (openedQueue->pop_front(tmp)) {
std::cout << "Received " << tmp << "\n";
}
}
void producer() {
boost_ipc::shared_memory_object::remove(SHMEMNAME);
boost_ipc::managed_shared_memory mysegment(boost_ipc::create_only, SHMEMNAME, 131072);
myqueue::ShmemAlloc alloc(mysegment.get_segment_manager());
myqueue*myQueue = mysegment.construct<myqueue>(SHQUEUENAME)(alloc);
char mychar='A';
for (int i = 0; i < 10; ++i)
myQueue->push_back(mychar);
while (myQueue->size() > 0)
continue;
}
int main()
{
//producer(); // delete comment for creating producer process
consumer();
return 0;
}
更新:
我可以用 MSVC15.3 和 Boost 1.64 重现它。
事实证明 vtable 指针是问题所在:它们在每个进程中都是不同的,一旦您拥有运行时多态类型 (std::is_polymorphic<T>
),就会导致未定义的行为。
原来文档明确禁止了:Is it possible to store polymorphic class in shared memory?
我根据 boost 示例在共享内存中实现了一个线程安全队列-class。对于一个简单的 producer/consumer 模型,它按预期工作。
作为下一步,我定义了由 ConcurrentIPCQueue
class 实现的接口 IConcurrentIPCQueue
。我需要这个接口,这样我就可以为另一个问题实现一个队列适配器。
我的第一个版本和下面的当前版本之间的唯一区别如下:
第一个版本:
template <class T> class ConcurrentIPCQueue
现在添加信息,我想实现这样的接口:
当前版本:
`template <class T> class ConcurrentIPCQueue :public IConcurrentIPCQueue<T>`
导致消费者端读取访问冲突。在生产者方面,我可以轻松地自己正确地 push_back
和 pop_front
数据。但奇怪的是,在消费者方面,我无法访问共享内存(尽管来自 segment.find returns 的对正确地是一个地址和 1)。
所以问题是,为什么实现接口的版本会在消费者方面产生影响并导致这个奇怪的错误。我该如何解决?
为了使示例简短,我在这里展示了一个极简的队列模型:
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/containers/deque.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/sync/interprocess_condition.hpp>
#include <boost/interprocess/sync/interprocess_mutex.hpp>
#include <boost/thread/lock_guard.hpp>
#include <sstream>
namespace boost_ipc = boost::interprocess;
static char const *SHMEMNAME= "SHMEM";
static char const *SHQUEUENAME= "MYQUEUE";
template <class T> class IConcurrentIPCQueue
{
public:
virtual void push_back(T const & data) = 0;
virtual bool pop_front(T & data) = 0;
virtual unsigned int size() = 0;
};
template <class T> class ConcurrentIPCQueue :public IConcurrentIPCQueue<T>
{
public:
// allocator for allocating memory from the shared memory
typedef boost_ipc::allocator<T, boost_ipc::managed_shared_memory::segment_manager> ShmemAlloc;
typedef boost_ipc::interprocess_mutex IPC_Mutex;
typedef boost_ipc::interprocess_condition IPC_Cond;
typedef boost::lock_guard<IPC_Mutex> LockGuard;
ConcurrentIPCQueue(ShmemAlloc salloc) : mQueue_(salloc) { }
void push_back(T const & data)
{
{
LockGuard lock(mMutex_);
mQueue_.push_back(data);
}
mWait_.notify_one();
}
bool pop_front(T & data)
{
LockGuard lock(mMutex_);
if (mQueue_.empty())
return false;
data = mQueue_.front(); // return reference to first element
mQueue_.pop_front(); // remove the first element
return true;
}
unsigned int size()
{
LockGuard lock(mMutex_);
return mQueue_.size();
}
private:
boost_ipc::deque<T, ShmemAlloc> mQueue_;
IPC_Mutex mMutex_;
IPC_Cond mWait_;
};
typedef ConcurrentIPCQueue<char> myqueue;
void consumer()
{
boost_ipc::managed_shared_memory openedSegment(boost_ipc::open_only, SHMEMNAME);
myqueue*openedQueue = openedSegment.find<myqueue>(SHQUEUENAME).first;
char tmp;
while (openedQueue->pop_front(tmp)) {
std::cout << "Received " << tmp << "\n";
}
}
void producer() {
boost_ipc::shared_memory_object::remove(SHMEMNAME);
boost_ipc::managed_shared_memory mysegment(boost_ipc::create_only, SHMEMNAME, 131072);
myqueue::ShmemAlloc alloc(mysegment.get_segment_manager());
myqueue*myQueue = mysegment.construct<myqueue>(SHQUEUENAME)(alloc);
char mychar='A';
for (int i = 0; i < 10; ++i)
myQueue->push_back(mychar);
while (myQueue->size() > 0)
continue;
}
int main()
{
//producer(); // delete comment for creating producer process
consumer();
return 0;
}
更新:
我可以用 MSVC15.3 和 Boost 1.64 重现它。
事实证明 vtable 指针是问题所在:它们在每个进程中都是不同的,一旦您拥有运行时多态类型 (std::is_polymorphic<T>
),就会导致未定义的行为。
原来文档明确禁止了:Is it possible to store polymorphic class in shared memory?