首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 其他教程 > 操作系统 >

Glibc内存储器管理-ptmalloc2源代码分析(二十四)

2014-05-12 
Glibc内存管理--ptmalloc2源代码分析(二十四)5.6.7 grow_heap,shrink_heap,delete_heap,heap_trim?这几个

Glibc内存管理--ptmalloc2源代码分析(二十四)

5.6.7 grow_heap,shrink_heap,delete_heap,heap_trim

?

这几个函数实现sub_heap和增长和收缩,grow_heap()函数主要将sub_heap中可读可写区域扩大;shrink_heap()函数缩小sub_heap的虚拟内存区域,减小该sub_heap的虚拟内存占用量;delete_heap()为一个宏,如果sub_heap中所有的内存都空闲,使用该宏函数将sub_heap的虚拟内存还回给操作系统;heap_trim()函数根据sub_heap的top chunk大小调用shrink_heap()函数收缩sub_heap。

函数的实现代码如下:

static int#if __STD_Cgrow_heap(heap_info *h, long diff)#elsegrow_heap(h, diff) heap_info *h; long diff;#endif{  size_t page_mask = malloc_getpagesize - 1;  long new_size;  diff = (diff + page_mask) & ~page_mask;  new_size = (long)h->size + diff;  if((unsigned long) new_size > (unsigned long) HEAP_MAX_SIZE)    return -1;  if((unsigned long) new_size > h->mprotect_size) {    if (mprotect((char *)h + h->mprotect_size,                 (unsigned long) new_size - h->mprotect_size,                 PROT_READ|PROT_WRITE) != 0)      return -2;    h->mprotect_size = new_size;  }  h->size = new_size;  return 0;}

?Grow_heap()函数的实现比较简单,首先将要增加的可读可写的内存大小按照页对齐,然后计算sub_heap总的可读可写的内存大小new_size,判断new_size是否大于HEAP_MAX_SIZE,如果是,返回,否则判断new_size是否大于当前sub_heap的可读可写区域大小,如果否,调用mprotect()设置新增的区域可读可写,并更新当前sub_heap的可读可写区域的大小为new_size。最后将当前sub_heap的字段size更新为new_size。

?

Shrink_heap()函数的实现源代码如下:

static int#if __STD_Cshrink_heap(heap_info *h, long diff)#elseshrink_heap(h, diff) heap_info *h; long diff;#endif{  long new_size;  new_size = (long)h->size - diff;  if(new_size < (long)sizeof(*h))    return -1;  /* Try to re-map the extra heap space freshly to save memory, and     make it inaccessible. */#ifdef _LIBC  if (__builtin_expect (__libc_enable_secure, 0))#else  if (1)#endif    {      if((char *)MMAP((char *)h + new_size, diff, PROT_NONE,                      MAP_PRIVATE|MAP_FIXED) == (char *) MAP_FAILED)        return -2;      h->mprotect_size = new_size;    }#ifdef _LIBC  else    madvise ((char *)h + new_size, diff, MADV_DONTNEED);#endif  /*fprintf(stderr, "shrink %p %08lx\n", h, new_size);*/  h->size = new_size;  return 0;}

#define delete_heap(heap) \ do { \ if ((char *)(heap) + HEAP_MAX_SIZE == aligned_heap_area) \ aligned_heap_area = NULL; \ munmap((char*)(heap), HEAP_MAX_SIZE); \ } while (0)

static intinternal_function#if __STD_Cheap_trim(heap_info *heap, size_t pad)#elseheap_trim(heap, pad) heap_info *heap; size_t pad;#endif{ mstate ar_ptr = heap->ar_ptr; unsigned long pagesz = mp_.pagesize; mchunkptr top_chunk = top(ar_ptr), p, bck, fwd; heap_info *prev_heap; long new_size, top_size, extra; /* Can this heap go away completely? */ while(top_chunk == chunk_at_offset(heap, sizeof(*heap))) {

?每个非主分配区至少有一个sub_heap,每个非主分配区的第一个sub_heap中包含了一个heap_info的实例和malloc_state的实例,分主分配区中的其它sub_heap中只有一个heap_info实例,紧跟heap_info实例后,为可以用于分配的内存块。当当前非主分配区的top chunk与当前sub_heap的heap_info实例的结束地址相同时,意味着当前sub_heap中只有一个空闲chunk,没有已分配的chunk。所以可以将当前整个sub_heap都释放掉。

prev_heap = heap->prev;    p = chunk_at_offset(prev_heap, prev_heap->size - (MINSIZE-2*SIZE_SZ));    assert(p->size == (0|PREV_INUSE)); /* must be fencepost */    p = prev_chunk(p);    new_size = chunksize(p) + (MINSIZE-2*SIZE_SZ);    assert(new_size>0 && new_size<(long)(2*MINSIZE));    if(!prev_inuse(p))      new_size += p->prev_size;    assert(new_size>0 && new_size<HEAP_MAX_SIZE);    if(new_size + (HEAP_MAX_SIZE - prev_heap->size) < pad + MINSIZE + pagesz)      break;

?每个sub_heap的可读可写区域的末尾都有两个chunk用于fencepost,以64位系统为例,最后一个chunk占用的空间为MINSIZE-2*SIZE_SZ,为16B,最后一个chuk的size字段记录的前一个chunk为inuse状态,并标识当前chunk大小为0,倒数第二个chunk为inuse状态,这个chunk也是fencepost的一部分,这个chunk的大小为2*SIZE_SZ,为16B,所以用于fencepost的两个chunk的空间大小为32B。fencepost也有可能大于32B,第二个chunk仍然为16B,第一个chunk的大小大于16B,这种情况发生在topchunk的空间小于2*MINSIZE,大于MINSIZE,但对于一个完全空闲的sub_heap来说,top chunk的空间肯定大于2*MINSIZE,所以在这里不考虑这种情况。用于fencepost的chunk空间其实都是被分配给应用层使用的,new_size表示当前sub_heap中可读可写区域的可用空间,如果倒数第二个chunk的前一个chunk为空闲状态,当前sub_heap中可读可写区域的可用空间大小还需要加上这个空闲chunk的大小。如果new_size与sub_heap中剩余的不可读写的区域大小之和小于32+4K(64位系统),意味着前一个sub_heap的可用空间太少了,不能释放当前的sub_heap。

ar_ptr->system_mem -= heap->size;    arena_mem -= heap->size;    delete_heap(heap);    heap = prev_heap;    if(!prev_inuse(p)) { /* consolidate backward */      p = prev_chunk(p);      unlink(p, bck, fwd);    }    assert(((unsigned long)((char*)p + new_size) & (pagesz-1)) == 0);    assert( ((char*)p + new_size) == ((char*)heap + heap->size) );    top(ar_ptr) = top_chunk = p;    set_head(top_chunk, new_size | PREV_INUSE);/*check_chunk(ar_ptr, top_chunk);*/

?首先更新非主分配区的内存统计,然后调用delete_heap()宏函数释放该sub_heap,把当前heap设置为被释放sub_heap的前一个sub_heap,p指向的是被释放sub_heap的前一个sub_heap的倒数第二个chunk,如果p的前一个chunk为空闲状态,由于不可能出现多个连续的空闲chunk,所以将p设置为p的前一个chunk,也就是p指向空闲chunk,并将该空闲chunk从空闲chunk链表中移除,并将将该空闲chunk赋值给sub_heap的top chunk,并设置top chunk的size,标识top chunk的前一个chunk处于inuse状态。然后继续判断循环条件,如果循环条件不满足,退出循环,如果条件满足,继续对当前sub_heap进行收缩。

 }  top_size = chunksize(top_chunk);  extra = ((top_size - pad - MINSIZE + (pagesz-1))/pagesz - 1) * pagesz;  if(extra < (long)pagesz)    return 0;  /* Try to shrink. */  if(shrink_heap(heap, extra) != 0)    return 0;  ar_ptr->system_mem -= extra;  arena_mem -= extra;  /* Success. Adjust top accordingly. */  set_head(top_chunk, (top_size - extra) | PREV_INUSE);  /*check_chunk(ar_ptr, top_chunk);*/

?首先查看top chunk的大小,如果topchunk的大小减去pad和MINSIZE小于一页大小,返回退出,否则调用shrink_heap()函数对当前sub_heap进行收缩,将空闲的整数个页收缩掉,仅剩下不足一页的空闲内存,如果shrink_heap()失败,返回退出,否则,更新内存使用统计,更新top chunk的大小。

  return 1;}
?

?

?

热点排行