Libev源码分析02:Libev中的内存扩容方法

在Libev中,如果某种结构的数组需要扩容,它使用array_needsize宏进行处理,比如:

array_needsize (int, fdchanges, fdchangemax, fdchangecnt, EMPTY2);  

这就表示要将整型(int)数组fdchanges,由原来的fdchangemax个元素扩容为fdchangecnt,新扩容的内存空间使用EMPTR2进行初始化。
array_needsize宏定义如下:

#define array_needsize(type,base,cur,cnt,init)                           \  
  if (expect_false ((cnt) > (cur)))                                      \  
  {                                                                      \  
    int ecb_unused ocur_ = (cur);                                        \  
    (base) = (type *)array_realloc(sizeof(type), (base), &(cur), (cnt)); \  
    init ((base) + (ocur_), (cur) - ocur_);                              \  
  }  

base是type类型的数组,当前有cur个元素,需要调整到cnt个元素,新扩充的内存空间使用init函数初始化。
该宏的关键在于array_realloc函数,它的实现如下:

static void * array_realloc (int elem, void *base, int *cur, int cnt)  
{  
    *cur = array_nextsize (elem, *cur, cnt);  
    return ev_realloc (base, elem * *cur);  
}  

该函数中,首先使用array_nextsize计算最终的元素个数,然后调用ev_realloc申请空间。array_nextsize计算新元素个数的方法如下:

/* find a suitable new size for the given array, */  
/* hopefully by rounding to a nice-to-malloc size */  
int array_nextsize (int elem, int cur, int cnt)  
{  
  int ncur = cur + 1;    
  do  
    ncur <<= 1;  
  while (cnt > ncur);    
  /* if size is large, round to MALLOC_ROUND - 4 * longs to accommodate malloc overhead */  
  if (elem * ncur > MALLOC_ROUND - sizeof (void *) * 4)  
  {  
    ncur *= elem;  
    ncur = (ncur + elem + (MALLOC_ROUND - 1) + sizeof (void *) * 4) & ~(MALLOC_ROUND - 1);  
    ncur = ncur - sizeof (void *) * 4;  
    ncur /= elem;  
  }  
  return ncur;  
}  

该函数中,首先得到一个比cnt大的偶数ncur,如果ncur个元素占用的空间(elem* ncur + sizeof (void ) 4)大于MALLOC_ROUND(4096)个字节,则需要调整ncur。

这里之所以要加上sizeof(void ) 4,是因为malloc在申请空间时,除了申请的字节数之外,它还会在内存块之外加上额外的空间,记录当前内存块的信息,也就是sizeof (void ) 4个字节。
调整ncur的方法,主要是下面的语句:

ncur *= elem;  
ncur = (ncur + elem + (MALLOC_ROUND - 1) + sizeof (void *) * 4) & ~(MALLOC_ROUND - 1);  

它的主要作用,就是使得ncur向上调整成MALLOC_ROUND的倍数。这里的ncur代表的是最终申请空间的总字节数。因此,还需要将其调整为元素个数:

ncur = ncur - sizeof (void *) * 4;  
ncur /= elem;  

得到最终的元素个数之后,接下来就是调用ev_realloc申请空间了,它的实现如下:

static void *ev_realloc_emul (void *ptr, long size) EV_THROW  
{  
  /* some systems, notably openbsd and darwin, fail to properly 
   * implement realloc (x, 0) (as required by both ansi c-89 and 
   * the single unix specification, so work around them here. 
   * recently, also (at least) fedora and debian started breaking it, 
   * despite documenting it otherwise. 
   */    
  if (size)  
    return realloc (ptr, size);    
  free (ptr);  
  return 0;  
}  
static void *(*alloc)(void *ptr, long size) = ev_realloc_emul;    
void *ev_realloc (void *ptr, long size)  
{  
  ptr = alloc (ptr, size);    
  if (!ptr && size)  
  {  
    fprintf (stderr, "(libev) cannot allocate %ld bytes, aborting.", size);  
    abort ();  
  }  
  return ptr;  
}  

转载

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页