在上文中,对mBlk三元组的原理进行了简单的介绍。下面将简单介绍三元组的实现,对其部分代码进行分析。
mBlk是用C语言实现的,因此其中包含了大量复杂的指针操作和函数指针。
1、函数表poolFunc
poolFunc是一个结构体,其中定义了多个函数指针,用来操作存储池,包括初始化netPool,申请和归还mBlk、ClBlk和Cluster等。
系统定义的默认函数表如下:
LOCAL POOL_FUNC dfltFuncTbl = /* default pool function table */ { _poolInit, _mBlkFree, _clBlkFree, _clFree, _mBlkClFree, _mBlkGet, _clBlkGet, _clusterGet, _mClGet, _clPoolIdGet, //由于存在多个Cluster子池,因此申请Cluster时,必须先得到其对应的Cluster池 };
通过函数表中的函数指针 STATUS (*pInitRtn) (NET_POOL_ID pNetPool, M_CL_CONFIG * pMclBlkConfig, CL_DESC * pClDescTbl, int clDescTblNumEnt, BOOL fromKheap);来完成存储池的初始化。
默认情况下,函数指针指向_poolInit()函数。
LOCAL STATUS _poolInit ( NET_POOL_ID pNetPool, /* pointer to a net pool */ M_CL_CONFIG * pMclBlkConfig, /* pointer to mBlk,clBlk config */ CL_DESC * pClDescTbl, /* pointer to cluster desc table */ int clDescTblNumEnt, /* number of cluster desc entries */ BOOL fromKheap /* 1:KHEAP_ALLOC 0:malloc/calloc */ )_poolInit()通过传入的配置参数来完成初始化。
pMclBlkConfig结构体中存储着mBlk和ClBlk的配置信息:
typedef struct { int mBlkNum; /* number of mBlks */ int clBlkNum; /* number of cluster Blocks */ char * memArea; /* pre allocated memory area */ int memSize; /* pre allocated memory size */ } M_CL_CONFIG;其中保存着mBlk、ClBlk的个数,并为mBlk和ClBlk预分配了一定内存。
pClDescTbl则保存着各个Cluster池的配置信息:
typedef struct clDesc { int clSize; /* cluster type */ int clNum; /* number of clusters */ char * memArea; /* pre allocated memory area */ int memSize; /* pre allocated memory size */ } CL_DESC;pClDescTbl中的每一个元素都保存着对应的Cluster池的信息,包括Cluster的大小、个数,以及为当前Cluster池中所有Cluster预先分配的内存空间。
3、示例
一个简单的使用示例如下:
M_CL_CONFIG mClBlkConfig; CL_DESC clDescTbl[1]; //数组大小为1,说明只分配一个Cluster池 int clDescTblNumEnt = 1; NetPoolPtr = (NET_POOL_ID)malloc(sizeof(NET_POOL)); mClBlkConfig.mBlkNum = 2000;//2000个mBlk mClBlkConfig.clBlkNum= 1000; //1000个ClBlk mClBlkConfig.memSize = (mClBlkConfig.mBlkNum * (M_BLK_SZ + sizeof(void *))) + (mClBlkConfig.clBlkNum * CL_BLK_SZ); //mBlk和ClBlk将占用的空间大小 mClBlkConfig.memArea = (char*)malloc(mClBlkConfig.memSize); //为mBlk和ClBlk预先分配内存 ueMCLBlkMemArea=mClBlkConfig.memArea; memset(mClBlkConfig.memArea,0,mClBlkConfig.memSize); clDescTbl[0].clSize = 2048;//每个Cluster的大小 clDescTbl[0].clNum = 1000; clDescTbl[0].memSize = clDescTbl[0].clNum*(clDescTbl[0].clSize+sizeof(void *)); clDescTbl[0].memArea = (char*)malloc(clDescTbl[0].memSize);//为所有的Cluster预先分配内存 ueClMemArea=clDescTbl[0].memArea; memset(clDescTbl[0].memArea,0,clDescTbl[0].memSize); if(netPoolInit //调用函数,完成存储池的初始化 ( NetPoolPtr, /* pointer to a net pool */ &mClBlkConfig, /* pointer to a mBlk configuration */ clDescTbl, /* pointer to cluster desc table */ clDescTblNumEnt, /* number of cluster desc entries */ NULL /* pointer to pool function table */ ) == /*VXOK*/0) { printf("netpool initial OK!\n"); }
从上面的代码可以发现,为每个mBlk和ClBlk及Cluster结构分配内存时,都是在待分配内存的基础上加了一个sizeof(void *),通过这四个字节来指向具体的存储池。在mBlk和ClBlk中,这4个字节是指向其所在的netPool的,而在Cluster中,这4个字节是指向该Cluster所在的Cluster池的。
这样在释放资源时,就可以通过这4个字节找到其所在的存储池。
4、netPoolInit()的具体实现
用户首先配置好相应参数,然后调用netPoolInit()来完成存储池的初始化。
STATUS netPoolInit ( NET_POOL_ID pNetPool, /* pointer to a net pool */ M_CL_CONFIG * pMclBlkConfig, /* pointer to a mBlk configuration */ CL_DESC * pClDescTbl, /* pointer to cluster desc table */ int clDescTblNumEnt, /* number of cluster desc entries */ POOL_FUNC * pFuncTbl /* pointer to pool function table */ ) { if (pNetPool == NULL) return (VXERROR); if (pFuncTbl != NULL) pNetPool->pFuncTbl = pFuncTbl; else pNetPool->pFuncTbl = _pNetPoolFuncTbl; /* default func table */ return (poolInit (pNetPool, pMclBlkConfig, pClDescTbl, clDescTblNumEnt, FROM_HOMEPDHEAP)); /* allocate from the homePD's heap */ }由上可知,在netPoolInit()中,首先指定netPool的函数表,然后调用poolInit()。
poolInit()为一个宏定义,其实现如下:
#define poolInit(poolId,pMclBlkConfig,pClDescTbl,tblNumEnt,fromKheap) \ (*(((NET_POOL_ID)(poolId))->pFuncTbl->pInitRtn)) \ ((poolId), (pMclBlkConfig), (pClDescTbl), (tblNumEnt), \ (fromKheap))即在poolInit()中,通过netPool指定的函数表中的函数指针调用真正的初始化函数,系统默认的初始化函数为_poolInit()。
在_poolInit()中,则是按照mBlk、ClBlk、Cluster的顺序,依次初始化各个子池。
三个子池的初始化实现基本类似,下面以mBlk的初始化为例,进行简要介绍。
mBlk的初始化主要是调用下面两个函数来完成的:
if (_memPoolInit (pMclBlkConfig->mBlkNum, M_BLK_SZ, sizeof(void *),pMclBlkConfig->memArea) != VXOK) { goto poolInitError; } pNetPool->pmBlkHead = _mBlkCarve ( pNetPool,pMclBlkConfig->mBlkNum,pMclBlkConfig->memArea);其中在_memPoolInit()中,只是判断是否已经成功分配内存和内存是否对齐,若成功则继续,否则返回错误。
然后调用_mBlkCarve()将为mBlk分配的内存划分成mBlkNum份,并链接起来,形成一条mBlk链,然后返回链表头部,交给netPool管理。
在之前的示例中可以看到,为mBlk分配的内存总大小为:
mClBlkConfig.mBlkNum * (M_BLK_SZ + sizeof(void *))即每个mBlk的大小为(M_BLK_SZ+4),其中M_BLK_SZ=sizeof(mBlk),4字节用来指向存储池netPool,示意图如下:
_mBlkCarve()的实现如下:
LOCAL M_BLK_ID _mBlkCarve (NET_POOL_ID pNetPool, /* pointer to net pool */ int num, /* number of units to allocate */ char * pool /* pre allocated memory area */ ) { M_BLK_ID pMblk = NULL; int ix; int size; /* size of each unit */ M_BLK_ID * ppMblk; size = M_BLK_SZ + sizeof (void *); /* 初始化时每次应移动的大小*/ ppMblk = &pMblk;//pMblk用于保存mBlk形成的链表的首个元素 for (ix = 0; ix < num; ++ix) { *ppMblk = (M_BLK_ID) (pool + sizeof (void *));//mBlk空闲时,每个mBlk的起始4字节用于指向下一个mBlk,用于形成链表 *((NET_POOL_ID *)(pool)) = pNetPool;//头部4字节用于指向netPool (*ppMblk)->mBlkHdr.mType = MT_FREE;//此时mBlk为空闲 ppMblk = &((*ppMblk)->mBlkHdr.mNext); pool += size; } return (pMblk); }第一次循环时,ppMblk指向pMblk,
*ppMblk = (M_BLK_ID) (pool + sizeof (void *));执行后,将memArea偏移4字节后的地址赋给*ppMblk,即将其赋给pMblk,这样pMblk就指向了mBlk链的头部。
然后执行 ppMblk = &((*ppMblk)->mBlkHdr.mNext); ppMblk指向mBlk的next指针,然后pool向下移动size大小,即sizeof(MBLK)+4,然后执行*ppMblk = (M_BLK_ID) (pool + sizeof (void *)); 则将下一个mBlk的起始地址赋给了*ppMblk ,即上一个mBlk的next指针,这样就形成了一个mBlk链
左侧为mBlk的简单示意图,通过mBlkHdr中的成员变量next指针指向下一个mBlk,形成一条mBlk链。
右侧为对预先分配好的内存块memArea进行初始化后的结果,每个子块的起始4字节指向netPool,每个mBlk通过next指针形成链表,然后返回链表头部pMblk赋给 pNetPool->pmBlkHead,将mBlk链的管理权交给存储池netPool。
ClBlk和Cluster的初始化过程和结果与mBlk类似。
5、资源的申请和释放
用户通过调用netTupleGet()来申请一个完整的三元组,其中先分别申请mBlk、ClBlk和Cluster,然后再将它们串联在一起。
5.1、mBlk的申请与释放
netPool保存着mBlk、ClBlk和Cluster链表的头部,mBlk、ClBlk、Cluster的申请释放都十分类似,以mBlk的申请、释放为例进行说明。用户调用netMblkGet()来申请一个mBlk,在该函数内部,通过netPool函数表中的函数指针来调用_mBlkGet()真正的申请资源。_mBlkGet()的实现如下:
LOCAL M_BLK_ID _mBlkGet( NET_POOL_ID pNetPool, /* pointer to the net pool */ int canWait, /* M_WAIT/M_DONTWAIT */ UCHAR type /* mBlk type */ ) { M_BLK_ID pMblk = NULL; /* pointer to mbuf */ int level; /* level of interrupt */ reTry: level = intLock(); /* lock interrupts very briefly */ if ((pMblk = pNetPool->pmBlkHead) != NULL) //取出链表头部元素 { pNetPool->pmBlkHead = pMblk->mBlkHdr.mNext; //更新链表头部 pNetPool->pPoolStat->mTypes [MT_FREE]--; //更新统计信息 pNetPool->pPoolStat->mTypes [type]++; intUnlock (level); /* unlock interrupts */ pMblk->mBlkHdr.mType = type; pMblk->mBlkHdr.mNext = NULL; pMblk->mBlkHdr.mNextPkt = NULL; pMblk->mBlkHdr.mFlags = 0; } return (pMblk);//返回获取的mBlk }这样就从netPool中的mBlk子池中取出了一个mBlk。
mBlk的释放则是调用netMblkFree()将其归还到netPool中,其最终实现通过_mBlkFree()完成:
LOCAL void _mBlkFree( NET_POOL_ID pNetPool, /* pointer to the net pool */ M_BLK_ID pMblk /* mBlk to free */ ) { FAST int ms; pMblk->mBlkHdr.mNextPkt = NULL; ms = intLock (); pNetPool->pPoolStat->mTypes [pMblk->mBlkHdr.mType]--; pNetPool->pPoolStat->mTypes [MT_FREE]++; pMblk->mBlkHdr.mType = MT_FREE; pMblk->mBlkHdr.mNext = pNetPool->pmBlkHead; //将归还的元素插入到mBlk链的头部 pNetPool->pmBlkHead = pMblk; /* 更新netPool中保存的链表头部 */ intUnlock (ms); }ClBlk和Cluster的申请、释放与mBlk类似。
5.2、三元组的申请
实现如下:
M_BLK_ID netTupleGet( ) { M_BLK_ID pMblk = NULL; /* pointer to mBlk */ pMblk = mBlkGet (pNetPool, canWait, type); //首先申请一个mBlk /*mBlk申请成功后,调用mClGet()来申请一个ClBlk和Cluster,然后将三者绑定在一起*/ if (pMblk && (mClGet (pNetPool, pMblk, bufSize, canWait, bestFit)!= VXOK)) return (pMblk); }在mClGet()中,则是通过函数表中的_mClGet()函数来具体实现的
LOCAL STATUS _mClGet( ) { int log2Size; /* size of cluster to the base 2 */ CL_POOL_ID pClPool; /* pointer to the cluster pool */ CL_BUF_ID pClBuf = NULL; /* pointer to the cluster buffer */ CL_BLK_ID pClBlk = NULL; /* pointer to the cluster blk */ FAST int ms; /* integer for level */ if ((pMblk == NULL) || ((pClBlk = _clBlkGet (pNetPool, canWait)) == NULL))//申请一个ClBlk goto mClGetError; /* 省略。。。。 通过一系列计算得到log2Size,然后找到待申请Cluster大小对应的Cluster子池。pClHead即为该Cluster子池的链表头部 */ if ((pClBuf = pClPool->pClHead)) //然后从Cluster子池中取出一个Cluster { if ((pClPool->pClHead = pClBuf->pClNext) == NULL) { pNetPool->clMask &= ~(CL_LOG2_TO_CL_SIZE(pClPool->clLg2)); } pClPool->clNumFree--; pClPool->clUsage++; intUnlock (ms); } else //若Cluster申请失败,则返回错误 { pNetPool->pPoolStat->mDrops++; /* no. times failed to find space */ intUnlock (ms); errnoSet (S_netBufLib_NO_POOL_MEMORY); assert(0); goto mClGetError; } /*然后将mBlk、ClBlk、Cluster三者绑定到一起*/ pMblk->mBlkHdr.mData = (caddr_t) pClBuf; pMblk->mBlkHdr.mFlags |= M_EXT; pClBlk->clNode.pClBuf = (caddr_t) pClBuf; pClBlk->clSize = pClPool->clSize; pClBlk->pClFreeRtn = NULL; pClBlk->clRefCnt = 1; pMblk->pClBlk = pClBlk; return (VXOK); /* return VXOK */ mClGetError: return (VXERROR); /* return VXERROR */ }最终形成的三元组包链如下所示: