搜索
bottom↓
回复: 4

请教高人解读Z-Stack内存分配函数

[复制链接]

出0入0汤圆

发表于 2008-7-30 18:23:09 | 显示全部楼层 |阅读模式
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[idx] )
      {
        break;
      }
    }
    proCur[idx]++;
    if ( proMax[idx] < proCur[idx] )
    {
      proMax[idx] = proCur[idx];
    }
    proTot[idx]++;
  }
#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;
}

阿莫论坛20周年了!感谢大家的支持与爱护!!

曾经有一段真挚的爱情摆在我的面前,我没有珍惜,现在想起来,还好我没有珍惜……

出0入0汤圆

发表于 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[idx] )
                                {
                                        break;
                                }
                        }
                        proCur[idx]++;
                        if ( proMax[idx] < proCur[idx] )
                        {
                                proMax[idx] = proCur[idx];
                        }
                        proTot[idx]++;
                }
#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;
}

出0入0汤圆

 楼主| 发表于 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.
*
* @return  void
*/
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[idx] )
      {
        break;
      }
    }

    proCur[idx]--;
  }
#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.
}

出0入0汤圆

发表于 2008-7-31 21:11:46 | 显示全部楼层
比较奇怪,因为malloc过程中并没有对ff1进行调整。我没有zstack代码只能推断了。

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

出0入0汤圆

发表于 2014-7-28 17:57:01 | 显示全部楼层
牛人啊,没做过都能分析这么透···
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-8-26 13:15

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表