当前位置: 首页 > news >正文

自己怎么做云购网站吗海外网站怎么浏览

自己怎么做云购网站吗,海外网站怎么浏览,为什么就一个网站打不开,公司管理体系原文#xff1a;http://blog.csdn.net/zhzht19861011/article/details/51606068 内存管理对应用程序和操作系统来说都非常重要。现在很多的程序漏洞和运行崩溃都和内存分配使用错误有关。 FreeRTOS操作系统将内核与内存管理分开实现#xff0c;操作系统内核仅规定了必要的内…原文http://blog.csdn.net/zhzht19861011/article/details/51606068 内存管理对应用程序和操作系统来说都非常重要。现在很多的程序漏洞和运行崩溃都和内存分配使用错误有关。 FreeRTOS操作系统将内核与内存管理分开实现操作系统内核仅规定了必要的内存管理函数原型而不关心这些内存管理函数是如何实现的。这样做大有好处可以增加系统的灵活性不同的应用场合可以使用不同的内存分配实现选择对自己更有利的内存管理策略。比如对于安全型的嵌入式系统通常不允许动态内存分配那么可以采用非常简单的内存管理策略一经申请的内存甚至不允许被释放。在满足设计要求的前提下系统越简单越容易做的更安全。再比如一些复杂应用要求动态的申请、释放内存操作那么也可以设计出相对复杂的内存管理策略允许动态分配和动态释放。 FreeRTOS内核规定的几个内存管理函数原型为 void *pvPortMalloc( size_t xSize ) 内存申请函数void vPortFree( void *pv ) 内存释放函数void vPortInitialiseBlocks( void ) 初始化内存堆函数size_t xPortGetFreeHeapSize( void ) 获取当前未分配的内存堆大小size_t xPortGetMinimumEverFreeHeapSize( void )获取未分配的内存堆历史最小值 FreeRTOS提供了5种内存管理实现有简单也有复杂的可以应用于绝大多数场合。它们位于下载包目录...\FreeRTOS\Source\portable\MemMang中,文件名分别为heap_1.c、heap_2.c、heap_3.c、heap_4.c、heap_5.c。我在《FreeRTOS系列第8篇---FreeRTOS内存管理》这篇文章中介绍了这5种内存管理的特性以及各自应用的场合今天我们要分析它们的实现方法。FreeRTOS提供的内存管理都是从内存堆中分配内存的。默认情况下FreeRTOS内核创建任务、队列、信号量、事件组、软件定时器都是借助内存管理函数从内存堆中分配内存。最新的FreeRTOS版本V9.0.0及其以上版本可以完全使用静态内存分配方法也就是不使用任何内存堆。对于heap_1.c、heap_2.c和heap_4.c这三种内存管理策略内存堆实际上是一个很大的数组定义为static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];其中宏configTOTAL_HEAP_SIZE用来定义内存堆的大小这个宏在FreeRTOSConfig.h中设置。对于heap_3.c这种策略只是简单的包装了标准库中的malloc()和free()函数包装后的malloc()和free()函数具备线程保护。因此内存堆需要通过编译器或者启动文件设置堆空间。heap_5.c比较有趣它允许程序设置多个非连续内存堆比如需要快速访问的内存堆设置在片内RAM稍微慢速访问的内存堆设置在外部RAM。每个内存堆的起始地址和大小由应用程序设计者定义。1. heap_1.c 这是5个内存管理策略中最简单的一个我们称为第一个内存管理策略它简单到只能申请内存。是的跟你想的一样一旦申请成功后这块内存再也不能被释放。对于大多数嵌入式系统特别是对安全要求高的嵌入式系统这种内存管理策略很有用因为对系统软件来说逻辑越简单越容易兼顾安全。实际上大多数的嵌入式系统并不需要动态删除任务、信号量、队列等而是在初始化的时候一次性创建好便一直使用永远不用删除。所以这个内存管理策略实现简洁、安全可靠使用的非常广泛。我对这个对内存管理策略也情有独钟。         我们可以将第一种内存管理看作是切面包初始化的内存就像一根完整的长棍面包每次申请内存就从一端切下适当长度的面包返还给申请者直到面包被分配完毕就这么简单。 这个内存管理策略使用两个局部静态变量来跟踪内存分配变量定义为 [objc] view plaincopy print? static size_t xNextFreeByte  ( size_t ) 0;   static uint8_t *pucAlignedHeap  NULL;   其中变量xNextFreeByte记录已经分配的内存大小用来定位下一个空闲的内存堆位置。因为内存堆实际上是一个大数组我们只需要知道已分配内存的大小就可以用它作为偏移量找到未分配内存的起始地址。变量xNextFreeByte被初始化为0然后每次申请内存成功后都会增加申请内存的字节数目。变量pucAlignedHeap指向对齐后的内存堆起始位置。为什么要对齐这是因为大多数硬件访问内存对齐的数据速度会更快。为了提高性能FreeRTOS会进行对齐操作不同的硬件架构对齐操作也不尽相同对于Cortex-M3架构进行8字节对齐。我们来看一下第一种内存管理策略对外提供的API函数。1.1内存申请pvPortMalloc() 函数源码为 [objc] view plaincopy print? voidvoid *pvPortMalloc( size_t xWantedSize )   {   voidvoid *pvReturn  NULL;   static uint8_t *pucAlignedHeap  NULL;             /* 确保申请的字节数是对齐字节数的倍数 */       #if( portBYTE_ALIGNMENT ! 1 )       {           if( xWantedSize  portBYTE_ALIGNMENT_MASK )           {               xWantedSize  ( portBYTE_ALIGNMENT - ( xWantedSize  portBYTE_ALIGNMENT_MASK ) );           }       }       #endif             vTaskSuspendAll();       {           if( pucAlignedHeap  NULL )           {               /* 第一次使用,确保内存堆起始位置正确对齐 */               pucAlignedHeap  ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE ) ucHeap[ portBYTE_ALIGNMENT ] )  ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );           }                 /* 边界检查,变量xNextFreeByte是局部静态变量,初始值为0 */           if( ( ( xNextFreeByte  xWantedSize )  configADJUSTED_HEAP_SIZE )                ( ( xNextFreeByte  xWantedSize )  xNextFreeByte ) )           {               /* 返回申请的内存起始地址并更新索引 */               pvReturn  pucAlignedHeap  xNextFreeByte;               xNextFreeByte  xWantedSize;           }       }       ( void ) xTaskResumeAll();             #if( configUSE_MALLOC_FAILED_HOOK  1 )       {           if( pvReturn  NULL )           {               extern void vApplicationMallocFailedHook( void );               vApplicationMallocFailedHook();           }       }       #endif             return pvReturn;   }   函数一开始会将申请的内存数量调整到对齐字节数的整数倍所以实际分配的内存空间可能比申请内存大。比如对于8字节对齐的系统申请11字节内存经过对齐后实际分配的内存是16字节8的整数倍。接下来会挂起所有任务因为内存申请是不可重入的使用了静态变量。 如果是第一次执行这个函数需要将变量pucAlignedHeap指向内存堆区域第一个地址对齐处。我们上面说内存堆其实是一个大数组编译器为这个数组分配的起始地址是随机的可能不符合我们的对齐需要这时候要进行调整。比如内存堆数组ucHeap从RAM地址0x10002003处开始系统按照8字节对齐则对齐后的内存堆如图1-1所示图1-1内存堆大小与地址对齐示意图之后进行边界检查查看剩余的内存堆是否够分配检查xNextFreeByte xWantedSize是否溢出。如果检查通过则为申请者返回有效的内存指针并更新已分配内存数量计数器xNextFreeByte从指针pucAlignedHeap开始偏移量为xNextFreeByte处的内存区域为未分配的内存堆起始位置。比如我们首次调用内存分配函数pvPortMalloc(20)申请20字节内存。根据对齐原则我们会实际申请到24字节内存申请成功后内存堆示意图如图1-2所示。图1-2第一次分配内存后的内存堆空间示意图内存分配完成后不管有没有分配成功都恢复之前挂起的调度器。如果内存分配不成功这里最可能是内存堆空间不够用了会调用一个钩子函数vApplicationMallocFailedHook()。这个钩子函数由应用程序提供通常我们可以打印内存分配设备信息或者点亮也故障指示灯。1.2获取当前未分配的内存堆大小xPortGetFreeHeapSize() 函数用于返回未分配的内存堆大小。这个函数也很有用通常用于检查我们设置的内存堆是否合理通过这个函数我们可以估计出最坏情况下需要多大的内存堆以便合理的节省RAM。 对于第一个内存管理策略这个函数实现十分简单源码如下 [objc] view plaincopy print? size_t xPortGetFreeHeapSize( void )   {       return ( configADJUSTED_HEAP_SIZE - xNextFreeByte );   }   从图1-1和图1-2我们知道宏configADJUSTED_HEAP_SIZE表示内存堆有效的大小这个值减去已经分配出去的内存大小正是我们需要的未分配的内存堆大小。1.3其它函数 第一个内存管理策略中还有两个函数vPortFree()和vPortInitialiseBlocks()。但实际上第一个函数什么也不做第二个函数仅仅将静态局部变量xNextFreeByte设置为0。 2. heap_2.c 第二种内存管理策略要比第一种内存管理策略复杂它使用一个最佳匹配算法允许释放之前已分配的内存块但是它不会把相邻的空闲块合成一个更大的块换句话说这会造成内存碎片。         这个内存管理策略用于重复的分配和删除具有相同堆栈空间的任务、队列、信号量、互斥量等等并且不考虑内存碎片的应用程序不适用于分配和释放随机字节堆栈空间的应用程序         与第一种内存管理策略一样内存堆仍然是一个大数组定义为 [objc] view plaincopy print? static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];   局部静态变量pucAlignedHeap指向对齐后的内存堆起始位置。地址对齐的原因在第一种内存管理策略中已经说明。假如内存堆数组ucHeap从RAM地址0x10002003处开始系统按照8字节对齐则对齐后的内存堆与第一个内存管理策略一样如图2-1所示图2-1内存堆示大小与地址对齐示意图2.1内存申请pvPortMalloc() 与第一种内存管理策略不同第二种内存管理策略使用一个链表结构来跟踪记录空闲内存块将空闲块组成一个链表。结构体定义为 [objc] view plaincopy print? typedef struct A_BLOCK_LINK   {       struct A_BLOCK_LINK *pxNextFreeBlock;   /*指向列表中下一个空闲块*/       size_t xBlockSize;                      /*当前空闲块的大小包括链表结构大小*/   } BlockLink_t;   两个BlockLink_t类型的局部静态变量xStart和xEnd用来标识空闲内存块的起始和结束。刚开始时整个内存堆有效空间就是一个空闲块如图2-2所示。因为要包含的信息越来越多我们必须舍弃一些信息舍弃的信息可以在上一幅图中找到。图2-2内存堆初始化示意图图2-2中的pvReturn是我自己增加的用于接下来分析内存申请操作堆栈初始化并没有这个变量也没有对其操作的代码。从图2-2中可以看出整个有效空间组成唯一一个空闲块在空闲块的起始位置放置了一个链表结构用于存储这个空闲块的大小和下一个空闲块的地址。由于目前只有一个空闲块所以空闲块的pxNextFreeBlock指向链表xEnd而链表xStart结构的pxNextFreeBlock指向空闲块。这样xStart、空闲块和xEnd组成一个单链表xStart表示链表头xEnd表示链表尾。随着内存申请和释放空闲块可能会越来越多但它们仍是以xStart链表开头以xEnd链表结尾根据空闲块的大小排序小的在前大的在后我们在内存释放一节中会给出示意图。当申请N字节内存时实际上不仅需要分配N字节内存还要分配一个BlockLink_t类型结构体空间用于描述这个内存块结构体空间位于空闲内存块的最开始处。当然和第一种内存管理策略一样申请的内存大小和BlockLink_t类型结构体大小都要向上扩大到对齐字节数的整数倍。我们看一下内存申请过程首先计算实际要分配的内存大小判断申请的内存是否合法。如果合法则从链表头xStart开始查找如果某个空闲块的xBlockSize字段大小能容得下要申请的内存则从这块内存取出合适的部分返回给申请者剩下的内存块组成一个新的空闲块按照空闲块的大小顺序插入到空闲块链表中小块在前大块在后。注意返回的内存中不包括链表结构而是紧邻链表结构经过对齐后面的位置。举个例子如图2-2所示的内存堆当调用申请内存函数如果内存堆空间足够大就将pvReturn指向的地址返回给申请者而不是静态变量pucAlignedHeap指向的内存堆起始位置当多次调用内存申请函数后没有调用内存释放函数内存堆结构如图2-3所示。注意图中的pvReturn仍是我自己增加上去的pvReturn指向的位置返回给申请者。后面我们讲内存释放时就是根据这个地址完成内存释放工作的。图2-3经过两次内存分配后的内存堆示意图有了上面的这些基础知识再看内存申请函数源码就比较简单了我把需要注意的要点以注释的方式放在源码中不再单独对这个函数做讲解值得注意的是函数中使用的一个静态局部变量xFreeBytesRemaining它用来记录未分配的内存堆大小。这个变量将提供给函数xPortGetFreeHeapSize()使用以方便用户估算内存堆使用情况。[objc] view plaincopy print? voidvoid *pvPortMalloc( size_t xWantedSize )   {   BlockLink_t *pxBlock, *pxPreviousBlock, *pxNewBlockLink;   static BaseType_t xHeapHasBeenInitialised  pdFALSE;   voidvoid *pvReturn  NULL;             /* 挂起调度器 */       vTaskSuspendAll();       {           /* 如果是第一次调用内存分配函数,这里先初始化内存堆,如图2-2所示 */           if( xHeapHasBeenInitialised  pdFALSE )           {               prvHeapInit();               xHeapHasBeenInitialised  pdTRUE;           }                 /* 调整要分配的内存值,需要增加上链表结构体空间,heapSTRUCT_SIZE表示经过对齐扩展后的结构体大小 */           if( xWantedSize  0 )           {               xWantedSize  heapSTRUCT_SIZE;                     /* 调整实际分配的内存大小,向上扩大到对齐字节数的整数倍 */               if( ( xWantedSize  portBYTE_ALIGNMENT_MASK ) ! 0 )               {                   xWantedSize  ( portBYTE_ALIGNMENT - ( xWantedSize  portBYTE_ALIGNMENT_MASK ) );               }           }                      if( ( xWantedSize  0 )  ( xWantedSize  configADJUSTED_HEAP_SIZE ) )           {               /* 空闲内存块是按照块的大小排序的,从链表头xStart开始,小的在前大的在后,以链表尾xEnd结束 */               pxPreviousBlock  xStart;               pxBlock  xStart.pxNextFreeBlock;               /* 搜索最合适的空闲块 */               while( ( pxBlock-xBlockSize  xWantedSize )  ( pxBlock-pxNextFreeBlock ! NULL ) )               {                   pxPreviousBlock  pxBlock;                   pxBlock  pxBlock-pxNextFreeBlock;               }                     /* 如果搜索到链表尾xEnd,说明没有找到合适的空闲内存块,否则进行下一步处理 */               if( pxBlock ! xEnd )               {                   /* 返回内存空间,注意是跳过了结构体BlockLink_t空间. */                   pvReturn  ( voidvoid * ) ( ( ( uint8_t * ) pxPreviousBlock-pxNextFreeBlock )  heapSTRUCT_SIZE );                         /* 这个块就要返回给用户,因此它必须从空闲块中去除. */                   pxPreviousBlock-pxNextFreeBlock  pxBlock-pxNextFreeBlock;                         /* 如果这个块剩余的空间足够多,则将它分成两个,第一个返回给用户,第二个作为新的空闲块插入到空闲块列表中去*/                   if( ( pxBlock-xBlockSize - xWantedSize )  heapMINIMUM_BLOCK_SIZE )                   {                       /* 去除分配出去的内存,在剩余内存块的起始位置放置一个链表结构并初始化链表成员 */                       pxNewBlockLink  ( voidvoid * ) ( ( ( uint8_t * ) pxBlock )  xWantedSize );                             pxNewBlockLink-xBlockSize  pxBlock-xBlockSize - xWantedSize;                       pxBlock-xBlockSize  xWantedSize;                             /* 将剩余的空闲块插入到空闲块列表中,按照空闲块的大小顺序,小的在前大的在后 */                       prvInsertBlockIntoFreeList( ( pxNewBlockLink ) );                   }                   /* 计算未分配的内存堆大小,注意这里并不能包含内存碎片信息 */                   xFreeBytesRemaining - pxBlock-xBlockSize;               }           }                 traceMALLOC( pvReturn, xWantedSize );       }       ( void ) xTaskResumeAll();             #if( configUSE_MALLOC_FAILED_HOOK  1 )       {   /* 如果内存分配失败,调用钩子函数 */           if( pvReturn  NULL )           {               extern void vApplicationMallocFailedHook( void );               vApplicationMallocFailedHook();           }       }       #endif             return pvReturn;   }   2.2内存释放vPortFree() 因为不需要合并相邻的空闲块第二种内存管理策略的内存释放也非常简单根据传入的参数找到链表结构然后将这个内存块插入到空闲块列表更新未分配的内存堆计数器大小结束。因为简单我们直接看源码。 [objc] view plaincopy print? void vPortFree( voidvoid *pv )   {   uint8_t *puc  ( uint8_t * ) pv;   BlockLink_t *pxLink;             if( pv ! NULL )       {           /* 根据传入的参数找到链表结构 */           puc - heapSTRUCT_SIZE;                 /* 预防某些编译器警告 */           pxLink  ( voidvoid * ) puc;                 vTaskSuspendAll();           {               /* 将这个块添加到空闲块列表 */               prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) );               /* 更新未分配的内存堆大小 */               xFreeBytesRemaining  pxLink-xBlockSize;                              traceFREE( pv, pxLink-xBlockSize );           }           ( void ) xTaskResumeAll();       }   }   我们举一个例子将图2-3 pvReturn指向的内存块释放掉假设configADJUSTED_HEAP_SIZE-40远大于要释放的内存块大小释放后的内存堆如图2-4所示图2-4释放内存后内存堆示意图从图2-4我们可以看出第二种内存管理策略的两个特点第一空闲块是按照大小排序的第二相邻的空闲块不会组合成一个大块。我们再接着引申讨论一下这种内存管理策略的优缺点。通过对内存申请和释放函数源码分析我们可以看出它的一个优点是速度足够快因为它的实现非常简单第二个优点是可以动态释放内存。但是它的缺点也非常明显由于在释放内存时不会将相邻的内存块合并所以这可能造成内存碎片。这就对其应用的场合要求极其苛刻第一每次创建或释放的任务、信号量、队列等必须大小相同如果分配或释放的内存是随机的绝对不可以用这种内存管理策略第二如果申请和释放的顺序不可预料也很危险。举个例子对于一个已经初始化的10KB内存堆先申请48字节内存然后释放再接着申请32字节内存那么一个本来48字节的大块就会被分为32字节和16字节的小块如果这种情况经常发生就会导致每个空闲块都可能很小最终在申请一个大块时就会因为没有合适的空闲块而申请失败并不是因为总的空闲内存不足2.3获取未分配的内存堆大小xPortGetFreeHeapSize() 函数用于返回未分配的内存堆大小。这个函数也很有用通常用于检查我们设置的内存堆是否合理通过这个函数我们可以估计出最坏情况下需要多大的内存堆以便进行合理的节省RAM。需要注意的是这个函数返回值并不能函数源码为 [objc] view plaincopy print? size_t xPortGetFreeHeapSize( void )   {       return xFreeBytesRemaining;   }   局部静态变量xFreeBytesRemaining在内存申请和内存释放函数中多次提到它用来动态记录未分配的内存堆大小。3.heap_3.c 第三种内存管理策略简单的封装了标准库中的malloc()和free()函数采用的封装方式是操作内存前挂起调度器、完成后再恢复调度器。封装后的malloc()和free()函数具备线程保护。 第一种和第二种内存管理策略都是通过定义一个大数组作为内存堆数组的大小由宏configTOTAL_HEAP_SIZE指定。第三种内存管理策略与前两种不同它不再需要通过数组定义内存堆而是需要使用编译器设置内存堆空间一般在启动代码中设置。因此宏configTOTAL_HEAP_SIZE对这种内存管理策略是无效的。 3.1内存申请pvPortMalloc() [objc] view plaincopy print? voidvoid *pvPortMalloc( size_t xWantedSize )   {   voidvoid *pvReturn;             vTaskSuspendAll();       {           pvReturn  malloc( xWantedSize );           traceMALLOC( pvReturn, xWantedSize );       }       ( void ) xTaskResumeAll();             #if( configUSE_MALLOC_FAILED_HOOK  1 )       {           if( pvReturn  NULL )           {               extern void vApplicationMallocFailedHook( void );               vApplicationMallocFailedHook();           }       }       #endif             return pvReturn;   }   3.2 内存释放vPortFree() [objc] view plaincopy print? void vPortFree( voidvoid *pv )   {       if( pv )       {           vTaskSuspendAll();           {               free( pv );               traceFREE( pv, 0 );           }           ( void ) xTaskResumeAll();       }   }   4.heap_4.c 第四种内存分配方法与第二种比较相似只不过增加了一个合并算法将相邻的空闲内存块合并成一个大块。 与第一种和第二种内存管理策略一样内存堆仍然是一个大数组定义为 [objc] view plaincopy print? static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];   4.1 内存申请pvPortMalloc() 和第二种内存管理策略一样它也使用一个链表结构来跟踪记录空闲内存块。结构体定义为 [objc] view plaincopy print? typedef struct A_BLOCK_LINK   {       struct A_BLOCK_LINK *pxNextFreeBlock;   /*指向列表中下一个空闲块*/       size_t xBlockSize;                      /*当前空闲块的大小包括链表结构大小*/   } BlockLink_t;   与第二种内存管理策略一样空闲内存块也是以单链表的形式组织起来的BlockLink_t类型的局部静态变量xStart表示链表头但第四种内存管理策略的链表尾保存在内存堆空间最后位置并使用BlockLink_t指针类型局部静态变量pxEnd指向这个区域第二种内存管理策略使用静态变量xEnd表示链表尾如图4-1所示。第四种内存管理策略和第二种内存管理策略还有一个很大的不同是第四种内存管理策略的空闲块链表不是以内存块大小为存储顺序而是以内存块起始地址大小为存储顺序地址小的在前地址大的在后。这也是为了适应合并算法而作的改变。图4-1内存堆初始化示意图从图4-1中可以看出整个有效空间组成唯一一个空闲块在空闲块的起始位置放置了一个链表结构用于存储这个空闲块的大小和下一个空闲块的地址。由于目前只有一个空闲块所以空闲块的pxNextFreeBlock指向指针pxEnd指向的位置而链表xStart结构的pxNextFreeBlock指向空闲块。xStart表示链表头pxEnd指向位置表示链表尾。当申请x字节内存时实际上不仅需要分配x字节内存还要分配一个BlockLink_t类型结构体空间用于描述这个内存块结构体空间位于空闲内存块的最开始处。当然和第一种、第二种内存管理策略一样申请的内存大小和BlockLink_t类型结构体大小都要向上扩大到对齐字节数的整数倍。我们先说一下内存申请过程首先计算实际要分配的内存大小判断申请内存合法性如果合法则从链表头xStart开始查找如果某个空闲块的xBlockSize字段大小能容得下要申请的内存则将这块内存取出合适的部分返回给申请者剩下的内存块组成一个新的空闲块按照空闲块起始地址大小顺序插入到空闲块链表中地址小的在前地址大的在后。在插入到空闲块链表的过程中还会执行合并算法判断这个块是不是可以和上一个空闲块合并成一个大块如果可以则合并然后再判断能不能和下一个空闲块合并成一个大块如果可以则合并合并算法是第四种内存管理策略和第二种内存管理策略最大的不同!经过几次内存申请和释放后可能的内存堆如图4-2所示图4-2经过数次内存申请和释放后某个内存堆示意图有了上面的基础我们再来看一下源码我把需要注意的要点以注释的方式放在源码中不再单独对这个函数做讲解。函数中会用到几个局部静态变量在这里简单说明一下xFreeBytesRemaining表示当前未分配的内存堆大小xMinimumEverFreeBytesRemaining表示未分配内存堆空间历史最小值。这个值跟xFreeBytesRemaining有很大区别只有记录未分配内存堆的最小值才能知道最坏情况下内存堆的使用情况。xBlockAllocatedBit这个变量在第一次调用内存申请函数时被初始化将它能表示的数值的最高位置1。比如对于32位系统这个变量被初始化为0x80000000最高位为1。内存管理策略使用这个变量来标识一个内存块是否空闲。如果内存块被分配出去则内存块链表结构成员xBlockSize按位或上这个变量即xBlockSize最高位置1在释放一个内存块时会把xBlockSize的最高位清零。 [objc] view plaincopy print? voidvoid *pvPortMalloc( size_t xWantedSize )   {   BlockLink_t *pxBlock, *pxPreviousBlock, *pxNewBlockLink;   voidvoid *pvReturn  NULL;             vTaskSuspendAll();       {           /* 如果是第一次调用内存分配函数,则初始化内存堆,初始化后的内存堆如图4-1所示 */           if( pxEnd  NULL )           {               prvHeapInit();           }                 /* 申请的内存大小合法性检查:是否过大.结构体BlockLink_t中有一个成员xBlockSize表示块的大小,这个成员的最高位被用来标识这个块是否空闲.因此要申请的块大小不能使用这个位.*/           if( ( xWantedSize  xBlockAllocatedBit )  0 )           {               /* 计算实际要分配的内存大小,包含链接结构体BlockLink_t在内,并且要向上字节对齐 */               if( xWantedSize  0 )               {                   xWantedSize  xHeapStructSize;                         /* 对齐操作,向上扩大到对齐字节数的整数倍 */                   if( ( xWantedSize  portBYTE_ALIGNMENT_MASK ) ! 0x00 )                   {                       xWantedSize  ( portBYTE_ALIGNMENT - ( xWantedSize  portBYTE_ALIGNMENT_MASK ) );                       configASSERT( ( xWantedSize  portBYTE_ALIGNMENT_MASK )  0 );                   }               }                     if( ( xWantedSize  0 )  ( xWantedSize  xFreeBytesRemaining ) )               {                   /* 从链表xStart开始查找,从空闲块链表(按照空闲块地址顺序排列)中找出一个足够大的空闲块 */                   pxPreviousBlock  xStart;                   pxBlock  xStart.pxNextFreeBlock;                   while( ( pxBlock-xBlockSize  xWantedSize )  ( pxBlock-pxNextFreeBlock ! NULL ) )                   {                       pxPreviousBlock  pxBlock;                       pxBlock  pxBlock-pxNextFreeBlock;                   }                         /* 如果最后到达结束标识,则说明没有合适的内存块,否则,进行内存分配操作*/                   if( pxBlock ! pxEnd )                   {                       /* 返回分配的内存指针,要跳过内存开始处的BlockLink_t结构体 */                       pvReturn  ( voidvoid * ) ( ( ( uint8_t * ) pxPreviousBlock-pxNextFreeBlock )  xHeapStructSize );                             /* 将已经分配出去的内存块从空闲块链表中删除 */                       pxPreviousBlock-pxNextFreeBlock  pxBlock-pxNextFreeBlock;                             /* 如果剩下的内存足够大,则组成一个新的空闲块 */                       if( ( pxBlock-xBlockSize - xWantedSize )  heapMINIMUM_BLOCK_SIZE )                       {                           /* 在剩余内存块的起始位置放置一个链表结构并初始化链表成员 */                           pxNewBlockLink  ( voidvoid * ) ( ( ( uint8_t * ) pxBlock )  xWantedSize );                           configASSERT( ( ( ( size_t ) pxNewBlockLink )  portBYTE_ALIGNMENT_MASK )  0 );                                 pxNewBlockLink-xBlockSize  pxBlock-xBlockSize - xWantedSize;                           pxBlock-xBlockSize  xWantedSize;                                 /* 将剩余的空闲块插入到空闲块列表中,按照空闲块的地址大小顺序,地址小的在前,地址大的在后 */                           prvInsertBlockIntoFreeList( pxNewBlockLink );                       }                                              /* 计算未分配的内存堆空间,注意这里并不能包含内存碎片信息 */                       xFreeBytesRemaining - pxBlock-xBlockSize;                                              /* 保存未分配内存堆空间历史最小值 */                       if( xFreeBytesRemaining  xMinimumEverFreeBytesRemaining )                       {                           xMinimumEverFreeBytesRemaining  xFreeBytesRemaining;                       }                             /* 将已经分配的内存块标识为已分配 */                       pxBlock-xBlockSize | xBlockAllocatedBit;                       pxBlock-pxNextFreeBlock  NULL;                   }               }           }                 traceMALLOC( pvReturn, xWantedSize );       }       ( void ) xTaskResumeAll();             #if( configUSE_MALLOC_FAILED_HOOK  1 )       {   /* 如果内存分配失败,调用钩子函数 */           if( pvReturn  NULL )           {               extern void vApplicationMallocFailedHook( void );               vApplicationMallocFailedHook();           }           else           {               mtCOVERAGE_TEST_MARKER();           }       }       #endif             configASSERT( ( ( ( size_t ) pvReturn )  ( size_t ) portBYTE_ALIGNMENT_MASK )  0 );       return pvReturn;   }   4.2 内存释放vPortFree() 第四种内存管理策略的内存释放也比较简单根据传入的参数找到链表结构然后将这个内存块插入到空闲块列表需要注意的是在插入过程中会执行合并算法这个我们已经在内存申请中讲过了。最后是将这个内存块标志为“空闲”、更新未分配的内存堆大小结束。源代码如下 [objc] view plaincopy print? void vPortFree( voidvoid *pv )   {   uint8_t *puc  ( uint8_t * ) pv;   BlockLink_t *pxLink;             if( pv ! NULL )       {           /* 根据参数地址找出内存块链表结构 */           puc - xHeapStructSize;           pxLink  ( voidvoid * ) puc;                 /* 检查这个内存块确实被分配出去 */           if( ( pxLink-xBlockSize  xBlockAllocatedBit ) ! 0 )           {               if( pxLink-pxNextFreeBlock  NULL )               {                   /* 将内存块标识为空闲 */                   pxLink-xBlockSize  ~xBlockAllocatedBit;                         vTaskSuspendAll();                   {                       /* 更新未分配的内存堆大小 */                       xFreeBytesRemaining  pxLink-xBlockSize;                       traceFREE( pv, pxLink-xBlockSize );                       /* 将这个内存块插入到空闲块链表中,按照内存块地址大小顺序 */                       prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) );                   }                   ( void ) xTaskResumeAll();               }           }       }   }   如图4-2所示的内存堆示意图如果我们将32字节的“已分配空间2”释放由于这个内存块的上面和下面都是空闲块所以在将它插入到空闲块链表的过程在中会先和“剩余空闲块1”合并合并后的块再和“剩余空闲块2”合并这样组成一个大的空闲块如图4-3所示图4-3内存释放后会和相邻的空闲块合并4.3获取当前未分配的内存堆大小xPortGetFreeHeapSize() 在内存申请和内存释放函数中以及多次提到过变量xFreeBytesRemaining。它就是一个计数器不能说明内存堆碎片信息。 [objc] view plaincopy print? size_t xPortGetFreeHeapSize( void )   {       return xFreeBytesRemaining;   }   4.4获取未分配的内存堆历史最小值xPortGetFreeHeapSize() 在内存申请中讲解过变量xMinimumEverFreeBytesRemaining这个函数很有用通过这个函数我们可以估计出最坏情况下需要多大的内存堆从而辅助我们合理的设置内存堆大小。 [objc] view plaincopy print? size_t xPortGetMinimumEverFreeHeapSize( void )   {       return xMinimumEverFreeBytesRemaining;   }   5.heap_5.c 第五种内存管理策略允许内存堆跨越多个非连续的内存区并且需要显示的初始化内存堆除此之外其它操作都和第四种内存管理策略十分相似。 第一、第二和第四种内存管理策略都是利用一个大数组作为内存堆使用并且只需要应用程序指定数组的大小通过宏configTOTAL_HEAP_SIZE定义数组定义由内存管理策略实现。第五种内存管理策略有些不同首先它允许跨内存区定义多个内存堆比如在片内RAM中定义一个内存堆还可以在片外RAM再定义内存堆其次用户需要指定每个内存堆区域的起始地址和内存堆大小、将它们放在一个HeapRegion_t结构体类型数组中并需要在使用任何内存分配和释放操作前调用vPortDefineHeapRegions()函数初始化这些内存堆。 让我们看一个例子假设我们为内存堆分配两个内存块第一个内存块大小为0x10000字节起始地址为0x80000000第二个内存块大小为0xa0000字节,起始地址为0x90000000。HeapRegion_t结构体类型数组可以定义如下 [objc] view plaincopy print? HeapRegion_t xHeapRegions[]     {       { ( uint8_t * ) 0x80000000UL, 0x10000 },        { ( uint8_t * ) 0x90000000UL, 0xa0000 },        { NULL, 0 }                    };   两个内存块要按照地址顺序放入到数组中地址小的在前因此地址为0x80000000的内存块必须放数组的第一个位置。数组必须以使用一个NULL指针和0字节元素作为结束以便让内存管理程序知道何时结束。定义好内存堆数组后需要应用程序调用vPortDefineHeapRegions()函数初始化这些内存堆将它们组成一个链表以xStart链表结构开头以pxEnd指针指向的位置结束。我们看一下内存堆数组是如何初始化的以上面的内存堆数组为例初始化后的内存堆如图5-1所示32为平台sizeof(BlockLink_t)8字节。图5-1多个非连续内存区用作内存堆初始化示意图一旦内存堆初始化之后内存申请和释放都和第四种内存管理策略相同不再单独分析。
http://www.pierceye.com/news/187222/

相关文章:

  • 分类网站建设黄陌陌网站怎么做
  • 做网站大概多钱互联网广告投放
  • 信通网站开发中心qq说说赞在线自助下单网站
  • 搭建网站步骤做电影网站需要什么条件
  • 您网站建设动漫设计与制作 学校
  • 利用模板如何制作网站泰安整站优化
  • 网站开发与网站建设网站上的聊天框怎么做的
  • 任务网站(做任务学技能的)开发公司宣传册
  • 织梦搭建商城网站高端网站建设深圳
  • 做网站排名优化的公司无需下载直接登录qq手机版
  • 网站不备案不能访问吗wordpress主题开发404页面
  • 工作总结个人总结自动app优化下载
  • 网站开发推荐书籍比较大的外贸网站
  • 上饶建设网站郑州网
  • 做淘宝客网站一定要备案吗没有网站域名备案
  • 用QQ群做网站排名慈溪网站制作哪家最好
  • 兴宁市网站建设手工艺品网站建设策划书
  • flash做网站导航网站品牌建设流程
  • 公司建设网站属于什么费用网站打模块
  • 网站建设应注意的问题网站备案验证码错误
  • 网站核验点网站自己怎么做的
  • 购物网站建设平台canvas可画网页版
  • 企业信息平台系统网站推广优化建设
  • 免费网站模板制作自助建站上建的网站免费吗
  • 深圳市网站建设外包公司门户网站代码结构
  • 昆明做网站建设找谁最新版在线 网
  • 东昌府聊城网站建设网站广告做的好的企业案例分析
  • asp三层架构做网站网站开发前端基础
  • 医院网站建设方案策划书把网站做成app的软件下载
  • 网站建设实践报告3000字wordpress消息提示插件