Libev源码分析08: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;
}

PS:Libev的代码就分析到这了!

posted @ 2015-11-15 09:51  gqtc  阅读(409)  评论(0编辑  收藏  举报