1、内存管理器类CMemPoolMgr有一个成员m_MemoryUnitList,我们称作内存单元链表,每一个内存单元对象对应着一个正在下载的文件所使用的内存池(CMemPool)列表。当eMule客户端接收到其他客户端发来的文件数据时,内存管理器对象m_pMemoryPool,调用函数GetMemory()来分配内存。我们来看看GetMemory()
TByte* CMemPoolMgr::GetMemory(CPartFile* pPartFile, unsigned int size)
{
TByte* pData;
CMemoryUnit *pMemoryUnit = NULL;
// 首先检查内存单元链表中有没有pPartFile,
POSITION pos2, pos1 = m_MemoryUnitList.GetTailPosition();
//从后往前查找
for (; (pos2 = pos1) != NULL;)
{
m_MemoryUnitList.GetPrev(pos1);
if (m_MemoryUnitList.GetAt(pos2)->GetPartFile() == pPartFile)
{
pMemoryUnit = m_MemoryUnitList.GetAt(pos2);
break;
}
}
if (pMemoryUnit) //找到该文件对应的内存单元
{
//检查该内存单元的内存池链表中有没有空闲的并且不小于size的内存单元
if ((pData = pMemoryUnit->GetMemory(size)) != NULL)
{
return pData;
}
else //没有找到
{
//首先在内存管理器中查找有没有空闲内存池
//如果有,将该空闲内存池加入到该内存单元的内存池链表尾部
//如果没有,重新申请一段内存,分别加入内存管理器和该内存单元的内存池链表尾部
CMemPool * pMemPool = GetMemPool(pMemoryUnit, m_nPoolUnitSize);
//返回查找到的或申请的内存
return pMemPool->GetMemory(size);
}
}
else //没有找到
{
//重新申请一个内存单元
pMemoryUnit = new CMemoryUnit(this, pPartFile);
//加入到内存管理器的内存单元链表尾部
m_MemoryUnitList.AddTail(pMemoryUnit);
//同上
CMemPool * pMemPool = GetMemPool(pMemoryUnit, m_nPoolUnitSize);
return pMemPool->GetMemory(size);
}
}
2、再来看看内存池类CMemPool
内存池对象又若干个(默认是1M)大小相等的内存块(默认是11K)组成,其在构造函数中调用InitMemPool()完成内存分配。
void CMemPool::InitMemPool()
{
try
{
unsigned int blocksize = CalcBlockSize(m_nPoolSize);
PMemoryBlock pMemBlock = new MemoryBlock[blocksize];
//初始化所有内存块对象,加入链表中
for (unsigned int i = 0; i < blocksize; i++)
{
m_MemoryUnitList.AddTail(&pMemBlock[i]);
}
//申请内存
m_pPoolEntry = (TByte*) new TByte[m_nPoolSize];
POSITION pos2, pos1 = m_MemoryUnitList.GetHeadPosition();
//给所有内存块对象赋值
for (unsigned int i = 0; (pos2 = pos1) != NULL; i++)
{
m_MemoryUnitList.GetNext(pos1);
//内存块起始地址
m_MemoryUnitList.GetAt(pos2)->pData = (TByte*)(m_pPoolEntry + i * m_nBlockUnitSize);
//该内存块处可用内存大小
m_MemoryUnitList.GetAt(pos2)->DataSize = m_nPoolSize - i * m_nBlockUnitSize;
}
}
catch (...)
{
// TODO : Catch the Exception of Memory allocation
}
//初始化时,指针指向链表头部,标识所有内存可用
m_CurUnitPos = m_MemoryUnitList.GetHeadPosition();
}
当调用GetMemory(int nSize)分配内存时,首先计算要分配的内存块数目,然后从内存块链表的尾部开始查找第一个大于nSize的内存 块对象,如果没有找到,返回NULL标志失败;如果找到,返回该内存块的指针。