VxWorks中mBlk三元组的实现

在上文中,对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池
    };

2、存储池netPool的初始化

通过函数表中的函数指针 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预先分配的内存空间。
在_poolInit()函数中,则根据相应的配置信息,来将存储池中各个子池中元素链接成链表,并将相关信息存储在结构体netPool中,然后即可通过netPool来管理所有元素。

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,示意图如下:

VxWorks中mBlk三元组的实现_第1张图片

_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 */
    }
最终形成的三元组包链如下所示:



你可能感兴趣的:(VxWorks中mBlk三元组的实现)