请教高人解读Z-Stack内存分配函数
void *osal_mem_alloc( uint16 size ){
osalMemHdr_t *prev;
osalMemHdr_t *hdr;
halIntState_t intState;
uint16 tmp;
byte coal = 0;
#if ( OSALMEM_GUARD )
// Try to protect against premature use by HAL / OSAL.
if ( ready != OSALMEM_READY )
{
osal_mem_init();
}
#endif
OSALMEM_ASSERT( size );
size += HDRSZ;
// Calculate required bytes to add to 'size' to align to halDataAlign_t.
if ( sizeof( halDataAlign_t ) == 2 )
{
size += (size & 0x01);
}
else if ( sizeof( halDataAlign_t ) != 1 )
{
const byte mod = size % sizeof( halDataAlign_t );
if ( mod != 0 )
{
size += (sizeof( halDataAlign_t ) - mod);
}
}
HAL_ENTER_CRITICAL_SECTION( intState );// Hold off interrupts.
// Smaller allocations are first attempted in the small-block bucket.
if ( size <= OSALMEM_SMALL_BLKSZ )
{
hdr = ff1;
}
else
{
hdr = ff2;
}
tmp = *hdr;
do
{
if ( tmp & OSALMEM_IN_USE )
{
tmp ^= OSALMEM_IN_USE;
coal = 0;
}
else
{
if ( coal != 0 )
{
#if ( OSALMEM_METRICS )
blkCnt--;
blkFree--;
#endif
*prev += *hdr;
if ( *prev >= size )
{
hdr = prev;
tmp = *hdr;
break;
}
}
else
{
if ( tmp >= size )
{
break;
}
coal = 1;
prev = hdr;
}
}
hdr = (osalMemHdr_t *)((byte *)hdr + tmp);
tmp = *hdr;
if ( tmp == 0 )
{
hdr = NULL;
break;
}
} while ( 1 );
if ( hdr != NULL )
{
tmp -= size;
// Determine whether the threshold for splitting is met.
if ( tmp >= OSALMEM_MIN_BLKSZ )
{
// Split the block before allocating it.
osalMemHdr_t *next = (osalMemHdr_t *)((byte *)hdr + size);
*next = tmp;
*hdr = (size | OSALMEM_IN_USE);
#if ( OSALMEM_METRICS )
blkCnt++;
if ( blkMax < blkCnt )
{
blkMax = blkCnt;
}
memAlo += size;
#endif
}
else
{
#if ( OSALMEM_METRICS )
memAlo += *hdr;
blkFree--;
#endif
*hdr |= OSALMEM_IN_USE;
}
#if ( OSALMEM_METRICS )
if ( memMax < memAlo )
{
memMax = memAlo;
}
#endif
#if ( OSALMEM_PROFILER )
{
byte idx;
size = *hdr ^ OSALMEM_IN_USE;
for ( idx = 0; idx < OSALMEM_PROMAX; idx++ )
{
if ( size <= proCnt )
{
break;
}
}
proCur++;
if ( proMax < proCur )
{
proMax = proCur;
}
proTot++;
}
#endif
hdr++;
#if ( OSALMEM_PROFILER )
osal_memset( (byte *)hdr, OSALMEM_ALOC, (size - HDRSZ) );
/* A small-block could not be allocated in the small-block bucket.
* When this occurs significantly frequently, increase the size of the
* bucket in order to restore better worst case run times. Set the first
* profiling bucket size in proCnt[] to the small-block bucket size and
* divide proSmallBlkMiss by the corresponding proTot[] size to get % miss.
* Best worst case time on TrasmitApp was achieved at a 0-15% miss rate
* during steady state Tx load, 0% during idle and steady state Rx load.
*/
if ( (size <= OSALMEM_SMALL_BLKSZ) && (hdr > ff2) )
{
proSmallBlkMiss++;
}
#endif
}
HAL_EXIT_CRITICAL_SECTION( intState );// Re-enable interrupts.
return (void *)hdr;
} 貌似比较简单呀。我单凭你贴出来的代码分析的。
空闲空间被分成块。每个块的起始存放着自己的大小和是否被使用的标志。
分配内存的时候根据对齐调整后的size,到不同的内存区域去分配。把当前块的大小和需要分配的size比较,不够就把后面块的空间也算进来。直到可以分配。( do while循环)
分配完之后,如果得到空间大小和size的差值比较大,浪费了一些,就把浪费的空间也做成块。
返回给应用程序前hdr++,表示把大小和使用标志占用的空间保留起来。用户只使用后面的空间。
void *osal_mem_alloc( uint16 size )
{
osalMemHdr_t *prev;
osalMemHdr_t *hdr;
halIntState_t intState;
uint16 tmp;
byte coal = 0;
#if ( OSALMEM_GUARD )
// Try to protect against premature use by HAL / OSAL.
if ( ready != OSALMEM_READY )
{
osal_mem_init();
}
#endif
OSALMEM_ASSERT( size );
/* 调整size,留出管理空间,对齐 */
size += HDRSZ;
// Calculate required bytes to add to 'size' to align to halDataAlign_t.
if ( sizeof( halDataAlign_t ) == 2 )
{
size += (size & 0x01);
}
else if ( sizeof( halDataAlign_t ) != 1 )
{
const byte mod = size % sizeof( halDataAlign_t );
if ( mod != 0 )
{
size += (sizeof( halDataAlign_t ) - mod);
}
}
HAL_ENTER_CRITICAL_SECTION( intState );// Hold off interrupts.
// Smaller allocations are first attempted in the small-block bucket.
/* 不同大小范围的申请,到不同的区间去分配 */
if ( size <= OSALMEM_SMALL_BLKSZ )
{
hdr = ff1;
}
else
{
hdr = ff2;
}
/* 获取当前block的管理数据, 管理数据应该低位表示此区域大小
高位是flag,从这段程序来看就是一个MEM_IN_USE的标志
*/
tmp = *hdr;
do
{
/* 如果此区域正在使用,则跳过,*/
if ( tmp & OSALMEM_IN_USE )
{
tmp ^= OSALMEM_IN_USE; /* tmp = 此区域大小*/
/* 有可能在遇到此区域前,
分配器正在准备联合多个连续的区域组成足够大的空间,
但是中途遇到一个被使用的空间,因此需要重头开始联合
coal就是这个标志
*/
coal = 0;
}
else
{
if ( coal != 0 )
{
#if ( OSALMEM_METRICS )
blkCnt--;
blkFree--;
#endif
/* 累加大小 */
*prev += *hdr;
if ( *prev >= size )
{
/* 足够大了就结束分配 */
hdr = prev;
tmp = *hdr;
break;
}
}
else
{
/* 如果此区域够大,则分配结束 */
if ( tmp >= size )
{
break;
}
/* 不够大,准备联合后面的区域 coal=1表示开始累加后面的空间*/
coal = 1;
prev = hdr;
}
}
hdr = (osalMemHdr_t *)((byte *)hdr + tmp);
tmp = *hdr;
if ( tmp == 0 )
{
hdr = NULL;
break;
}
} while ( 1 );
/* 如果前面分配到了空间 */
if ( hdr != NULL )
{
tmp -= size;
/* tmp 现在是分配到的空间大小-实际需求大小,也就是浪费的空间大小 */
// Determine whether the threshold for splitting is met.
if ( tmp >= OSALMEM_MIN_BLKSZ )
{
/* 浪费太多的话,还是将其标识为自由空间 */
// Split the block before allocating it.
osalMemHdr_t *next = (osalMemHdr_t *)((byte *)hdr + size);
*next = tmp;
/* 把hdr调整为需求大小,然后打上正在使用的标志 */
*hdr = (size | OSALMEM_IN_USE);
#if ( OSALMEM_METRICS )
blkCnt++;
if ( blkMax < blkCnt )
{
blkMax = blkCnt;
}
memAlo += size;
#endif
}
else
{
#if ( OSALMEM_METRICS )
memAlo += *hdr;
blkFree--;
#endif
/* 浪费空间不算大,则直接打正在使用标志 */
*hdr |= OSALMEM_IN_USE;
}
#if ( OSALMEM_METRICS )
if ( memMax < memAlo )
{
memMax = memAlo;
}
#endif
#if ( OSALMEM_PROFILER )
{
byte idx;
size = *hdr ^ OSALMEM_IN_USE;
for ( idx = 0; idx < OSALMEM_PROMAX; idx++ )
{
if ( size <= proCnt )
{
break;
}
}
proCur++;
if ( proMax < proCur )
{
proMax = proCur;
}
proTot++;
}
#endif
hdr++;
#if ( OSALMEM_PROFILER )
osal_memset( (byte *)hdr, OSALMEM_ALOC, (size - HDRSZ) );
/* A small-block could not be allocated in the small-block bucket.
* When this occurs significantly frequently, increase the size of the
* bucket in order to restore better worst case run times. Set the first
* profiling bucket size in proCnt[] to the small-block bucket size and
* divide proSmallBlkMiss by the corresponding proTot[] size to get % miss.
* Best worst case time on TrasmitApp was achieved at a 0-15% miss rate
* during steady state Tx load, 0% during idle and steady state Rx load.
*/
if ( (size <= OSALMEM_SMALL_BLKSZ) && (hdr > ff2) )
{
proSmallBlkMiss++;
}
#endif
}
HAL_EXIT_CRITICAL_SECTION( intState );// Re-enable interrupts.
return (void *)hdr;
} 多谢。基本上明白了。
下面一个释放空间的函数,
//这里疑问,为什么要判断 ff1 > currHdr ???
if ( ff1 > currHdr ) //空闲空间指针>要释放的空间地址
{
ff1 = currHdr; //空闲空间指针指向要释放的空间地址
}
/*********************************************************************
* @fn osal_mem_free
*
* @brief Implementation of the de-allocator functionality.
*
* @param ptr - pointer to the memory to free.
*
* @returnvoid
*/
void osal_mem_free( void *ptr )
{
osalMemHdr_t *currHdr;
halIntState_t intState;
#if ( OSALMEM_GUARD )
// Try to protect against premature use by HAL / OSAL.
if ( ready != OSALMEM_READY )
{
osal_mem_init();
}
#endif
HAL_ENTER_CRITICAL_SECTION( intState );// Hold off interrupts.
OSALMEM_ASSERT( ptr );
currHdr = (osalMemHdr_t *)ptr - 1; //currHdr指向存储块头
// Has this block already been freed?
OSALMEM_ASSERT( *currHdr & OSALMEM_IN_USE );
*currHdr &= ~OSALMEM_IN_USE; //清使用标志位
#if ( OSALMEM_PROFILER )
{
uint16 size = *currHdr;
byte idx;
for ( idx = 0; idx < OSALMEM_PROMAX; idx++ )
{
if ( size <= proCnt )
{
break;
}
}
proCur--;
}
#endif
#if ( OSALMEM_METRICS )
memAlo -= *currHdr;
blkFree++;
#endif
//这里疑问,为什么要判断 ff1 > currHdr ???
if ( ff1 > currHdr ) //空闲空间指针>要释放的空间地址
{
ff1 = currHdr; //空闲空间指针指向要释放的空间地址
}
#if ( OSALMEM_PROFILER )
osal_memset( (byte *)currHdr+HDRSZ, OSALMEM_REIN, (*currHdr - HDRSZ) );
#endif
HAL_EXIT_CRITICAL_SECTION( intState );// Re-enable interrupts.
} 比较奇怪,因为malloc过程中并没有对ff1进行调整。我没有zstack代码只能推断了。
比如最开始ff1指向“堆”的起始地址,假设是 0x10000
我分配了3次0x100字节后,最前面的3个块实际上在下一次申请内存的时候是不必要再扫描的。应该将ff1指向0x10300。如果之后释放了0x10100开始的地址。则curHdr=0x10100< ff1。这样就表示在ff1之前已经有空闲空间了。下次分配的时候需要从前面扫描起。因此要讲ff1再向前移。 牛人啊,没做过都能分析这么透···
页:
[1]