ATmega32 发表于 2008-7-30 18:23:09

请教高人解读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;
}

lllaaa 发表于 2008-7-30 20:40:57

貌似比较简单呀。我单凭你贴出来的代码分析的。

空闲空间被分成块。每个块的起始存放着自己的大小和是否被使用的标志。

分配内存的时候根据对齐调整后的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;
}

ATmega32 发表于 2008-7-31 13:43:15

多谢。基本上明白了。

下面一个释放空间的函数,


//这里疑问,为什么要判断 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.
}

lllaaa 发表于 2008-7-31 21:11:46

比较奇怪,因为malloc过程中并没有对ff1进行调整。我没有zstack代码只能推断了。

比如最开始ff1指向“堆”的起始地址,假设是 0x10000
我分配了3次0x100字节后,最前面的3个块实际上在下一次申请内存的时候是不必要再扫描的。应该将ff1指向0x10300。如果之后释放了0x10100开始的地址。则curHdr=0x10100< ff1。这样就表示在ff1之前已经有空闲空间了。下次分配的时候需要从前面扫描起。因此要讲ff1再向前移。

Joinj 发表于 2014-7-28 17:57:01

牛人啊,没做过都能分析这么透···
页: [1]
查看完整版本: 请教高人解读Z-Stack内存分配函数