- 1 共享内存模型
- 2 获取共享内存
- 2.1 MemoryManager::getChunk
- 2.2 MemPool::getChunk
- 3 释放共享内存
- 3.1 SharedChunk::freeChunk
- 3.2 MemPool::freeChunk
- 4 总结
基于共享内存通信的核心在于共享内存的管理,包括共享内存的分配、释放。
1 共享内存模型
iceoryx先将整块共享内存粗略划分为多个MemPool
,每个MemPool
进一步划分为若干个相同大小的Chunk
。每个MemPool
中的Chunk
数量及大小可以由用户配置,也可以使用默认配置。iceoryx中分配、回收共享内存的单位是Chunk
,这种内存分配方式借鉴Linux内核中的Slab
机制。共享内存的具体划分如下图所示:
其中,FreeIndices
用于存放空闲Chunk
索引的队列,以便于内存分配和释放。从上图中,共享内存管理的三级结构依次为MemoryManager
→ MemPool
→ Chunk
。
2 获取共享内存
2.1 MemoryManager::getChunk
职责:
获取指定大小的Chunk
(裸指针),并包装为SharedChunk
。
返回:
SharedChunk
实例,类似智能指针,可以在适当的是好释放Chunk
。
expected<SharedChunk, MemoryManager::Error> MemoryManager::getChunk(const ChunkSettings& chunkSettings) noexcept
{void* chunk{nullptr};MemPool* memPoolPointer{nullptr};const auto requiredChunkSize = chunkSettings.requiredChunkSize();uint32_t aquiredChunkSize = 0U;for (auto& memPool : m_memPoolVector){uint32_t chunkSizeOfMemPool = memPool.getChunkSize();if (chunkSizeOfMemPool >= requiredChunkSize){chunk = memPool.getChunk();memPoolPointer = &memPool;aquiredChunkSize = chunkSizeOfMemPool;break;}}if (m_memPoolVector.size() == 0){IOX_LOG(FATAL) << "There are no mempools available!";errorHandler(iox::PoshError::MEPOO__MEMPOOL_GETCHUNK_CHUNK_WITHOUT_MEMPOOL, ErrorLevel::SEVERE);return err(Error::NO_MEMPOOLS_AVAILABLE);}else if (memPoolPointer == nullptr){IOX_LOG(FATAL) << "The following mempools are available:" << [this](auto& log) -> iox::log::LogStream& {this->printMemPoolVector(log);return log;} << "Could not find a fitting mempool for a chunk of size "<< requiredChunkSize;errorHandler(iox::PoshError::MEPOO__MEMPOOL_GETCHUNK_CHUNK_IS_TOO_LARGE, ErrorLevel::SEVERE);return err(Error::NO_MEMPOOL_FOR_REQUESTED_CHUNK_SIZE);}else if (chunk == nullptr){IOX_LOG(ERROR) << "MemoryManager: unable to acquire a chunk with a chunk-payload size of "<< chunkSettings.userPayloadSize()<< "The following mempools are available:" << [this](auto& log) -> iox::log::LogStream& {this->printMemPoolVector(log);return log;};errorHandler(iox::PoshError::MEPOO__MEMPOOL_GETCHUNK_POOL_IS_RUNNING_OUT_OF_CHUNKS, ErrorLevel::MODERATE);return err(Error::MEMPOOL_OUT_OF_CHUNKS);}else{auto chunkHeader = new (chunk) ChunkHeader(aquiredChunkSize, chunkSettings);auto chunkManagement = new (m_chunkManagementPool.front().getChunk())ChunkManagement(chunkHeader, memPoolPointer, &m_chunkManagementPool.front());return ok(SharedChunk(chunkManagement));}
}
逐段代码分析:
- LINE 09 ~ LINE 19: 查找第一个
Chunk
大小超过应用所需大小的MemPool
,并从中拿到空闲Chunk
指针。 - LINE 21 ~ LINE 50: 错误处理。
- LINE 53 ~ LINE 56:
2.2 MemPool::getChunk
职责:
从MemPool
中获取未使用的Chunk
。
返回:
指向该Chunk
的指针。
void* MemPool::getChunk() noexcept
{uint32_t l_index{0U};if (!m_freeIndices.pop(l_index)){IOX_LOG(WARN) << "Mempool [m_chunkSize = " << m_chunkSize << ", numberOfChunks = " << m_numberOfChunks<< ", used_chunks = " << m_usedChunks << " ] has no more space left";return nullptr;}/// @todo iox-#1714 verify that m_usedChunk is not changed during adjustMInFree/// without changing m_minFreem_usedChunks.fetch_add(1U, std::memory_order_relaxed);adjustMinFree();return m_rawMemory.get() + l_index * m_chunkSize;
}
整体代码分析
上述代码逻辑很简单,首先从m_freeIndices
中弹出一个空闲Chunk
的索引,正在使用的Chunk数量增1,根据索引计算并返回Chunk
指针。
3 释放共享内存
3.1 SharedChunk::freeChunk
void SharedChunk::freeChunk() noexcept
{m_chunkManagement->m_mempool->freeChunk(static_cast<void*>(m_chunkManagement->m_chunkHeader.get()));m_chunkManagement->m_chunkManagementPool->freeChunk(m_chunkManagement);m_chunkManagement = nullptr;
}
3.2 MemPool::freeChunk
职责:
从MemPool
中获取未使用的Chunk
。
返回:
指向该Chunk
的指针。
void MemPool::freeChunk(const void* chunk) noexcept
{cxx::Expects(m_rawMemory.get() <= chunk&& chunk <= m_rawMemory.get() + (static_cast<uint64_t>(m_chunkSize) * (m_numberOfChunks - 1U)));auto offset = static_cast<const uint8_t*>(chunk) - m_rawMemory.get();cxx::Expects(offset % m_chunkSize == 0);uint32_t index = static_cast<uint32_t>(offset / m_chunkSize);if (!m_freeIndices.push(index)){errorHandler(PoshError::POSH__MEMPOOL_POSSIBLE_DOUBLE_FREE);}m_usedChunks.fetch_sub(1U, std::memory_order_relaxed);
}
整体代码分析
根据相对首地址偏移和Chunk
大小计算索引,将索引推入空闲索引队列m_freeIndices
中,正在使用的Chunk
数减1。
4 总结
本文学习了底层共享内存管理模型以及具体的分配和释放逻辑,上层的通信模块对共享内存Chunk的使用后续展开。