阅读Sofia-SIP源码 - su模块 - su_alloc.c/h

在众多资料和文章中多次注意到出现了home based memory management。这应该是Sofia软件包的亮点之一。现在总算开始分析它了。


struct su_home_s {
  int         suh_size;
  su_block_t *suh_blocks;
  su_alock_t *suh_lock;
};
typedef struct {
  unsigned sua_size:SIZEBITS;	/**< Size of the block */
  unsigned sua_home:1;		/**< Is this another home? */
  unsigned :0;
  void    *sua_data;		/**< Data pointer */
} su_alloc_t;

struct su_block_s {
  su_home_t  *sub_parent;	/**< Parent home */
  char       *sub_preload;	/**< Preload area */
  su_home_stat_t *sub_stats;	/**< Statistics.. */
  void      (*sub_destructor)(void *); /**< Destructor function */
  size_t      sub_ref;		/**< Reference count */
#define REF_MAX SIZE_MAX
  size_t      sub_used;		/**< Number of blocks allocated */
  size_t      sub_n;		/**< Size of hash table  */

  unsigned    sub_prsize:16;	/**< Preload size */
  unsigned    sub_prused:16;	/**< Used from preload */
  unsigned    sub_hauto:1;      /**< "Home" is not from malloc */
  unsigned    sub_auto:1;	/**< struct su_block_s is not from malloc */
  unsigned    sub_preauto:1;	/**< Preload is not from malloc */
  unsigned    sub_auto_all:1;	/**< Everything is from stack! */
  unsigned :0;

  su_alloc_t  sub_nodes[SUB_N];	/**< Pointers to data/lower blocks */
};

从结构体类型来看,整个Home based memory management包括三个层级。最上层是su_home_s,中间一层是su_block_s,最下那层是su_alloc_t。su_home_s结构体保留一个指向su_block_s结构体的指针,su_block_s包含一个su_alloc_t类型的数组。

SU_HOME_INIT宏定义就是给一个su_home_s结构体变量赋初值。

#define SU_HOME_INIT(obj) { 0, NULL, NULL }


su.c源文件中的前部包含了一大段有关如何使用home based memory management的说明。先看使用说明再看内部细节实现,也许对于理解实现更有帮助。先将这段内容摘录如下:

 *
 * Typically, there is a @e home @e object which contains a su_home_t
 * structure in the beginning of the object (sort of inheritance from
 * su_home_t):
 * @code
 * struct context {
 *   su_home_t ctx_home[1];
 *   other_t  *ctx_stuff;
 *   ...
 * }
 * @endcode
 *
 * A new home memory pool can be created with su_home_new():
 * @code
 * struct context *ctx = su_home_new(sizeof (struct context));
 * @endcode
 *
 * It is also possible to create a secondary memory pool that can be
 * released separately:
 *
 * @code
 * struct context *ctx = su_home_clone(tophome, sizeof (struct context));
 * @endcode
 *
 * Note that the tophome has a reference to @a ctx structure; whenever
 * tophome is freed, the @a ctx is also freed.
 *
 * You can also create an independent home object by passing NULL as @a
 * tophome argument. This is identical to the call to su_home_new().
 *
 * The memory allocations using @a ctx proceed then as follows:
 * @code
 *    zeroblock = su_zalloc(ctx->ctx_home, sizeof (*zeroblock));
 * @endcode
 *
 * The home memory pool - the home object and all the memory blocks
 * allocated using it - are freed when su_home_unref() is called:
 *
 * @code
 *    su_home_unref(ctx->ctx_home).
 * @endcode
 *
 * @note For historical reasons, su_home_unref() is also known as
 * su_home_zap().
 *
 * As you might have guessed, it is also possible to use reference counting
 * with home objects. The function su_home_ref() increases the reference
 * count, su_home_unref() decreases it. A newly allocated or initialized
 * home object has reference count of 1.
 *
 * @note Please note that while it is possible to create new references to
 * secondary home objects which have a parent home, the secondary home
 * objects will always be destroyed when the parent home is destroyed even
 * if there are other references left to them.
 *
 * The memory blocks in a cloned home object are freed when the object with
 * home itself is freed:
 * @code
 *    su_free(tophome, ctx);
 * @endcode
 *
 * @note
 *
 * The su_home_destroy() function is deprecated as it does not free the home
 * object itself. Like su_home_deinit(), it should be called only on home
 * objects with reference count of 1.
 *
 * The function su_home_init() initializes a home object structure. When the
 * initialized home object is destroyed or deinitialized or its reference
 * count reaches zero, the memory allocate thorugh it reclaimed but the home
 * object structure itself is not freed.
 *
 * @section su_home_destructor_usage Destructors
 *
 * It is possible to give a destructor function to a home object. The
 * destructor releases other resources associated with the home object
 * besides memory. The destructor function will be called when the reference
 * count of home reaches zero (upon calling su_home_unref()) or the home
 * object is otherwise deinitialized (calling su_home_deinit() on
 * objects allocated from stack).
 *
 * @section su_home_move_example Combining Allocations
 *
 * In some cases, an operation that makes multiple memory allocations may
 * fail, making those allocations redundant. If the allocations are made
 * through a temporary home, they can be conveniently freed by calling
 * su_home_deinit(), for instance. If, however, the operation is successful,
 * and one wants to keep the allocations, the allocations can be combined
 * into an existing home with su_home_move(). For example,
 * @code
 * int example(su_home_t *home, ...)
 * {
 *   su_home_t temphome[1] = { SU_HOME_INIT(temphome) };
 *
 *   ... do lot of allocations with temphome ...
 *
 *   if (success)
 *     su_home_move(home, temphome);
 *   su_home_deinit(temphome);
 *
 *   return success;
 * }
 * @endcode
 *
 * Note that the @a temphome is deinitialized in every case, but when
 * operation is successful, the allocations are moved from @a temphome to @a
 * home.
 *
 * @section su_alloc_threadsafe Threadsafe Operation
 *
 * If multiple threads need to access same home object, it must be marked as
 * @e threadsafe by calling su_home_threadsafe() with the home pointer as
 * argument. The threadsafeness is not inherited by clones.
 *
 * The threadsafe home objects can be locked and unlocked with
 * su_home_mutex_lock() and su_home_mutex_unlock(). These operations are
 * no-op on home object that is not threadsafe.
 *
 * @section su_alloc_preloading Preloading a Memory Home
 *
 * In some situations there is quite heavy overhead if the global heap
 * allocator is used. The overhead caused by the large number of small
 * allocations can be reduced by using su_home_preload(): it allocates or
 * preloads some a memory to home to be used as a kind of private heap. The
 * preloaded memory area is then used to satisfy small enough allocations.
 * For instance, the SIP parser typically preloads some 2K of memory when it
 * starts to parse the message.
 *
 * @section su_alloc_stack Using Stack
 *
 * In some situation, it is sensible to use memory allocated from stack for
 * some operations. The su_home_auto() function can be used for that
 * purpose. The memory area from stack is used to satisfy the allocations as
 * far as possible; if it is not enough, allocation is made from heap.
 *
 * The word @e auto refers to the automatic scope; however, the home object
 * that was initialized with su_home_auto() must be explicitly deinitialized
 * with su_home_deinit() or su_home_unref() when the program exits the scope
 * where the stack frame used in su_home_auto() was allocated.
 */

使用方式分几步实现:

1、先申明一个包含有su_home_s结构体对象的结构体,类似于这样

struct context {
  su_home_t ctx_home[1];
  other_t  *ctx_stuff;
  ...
}

context结构体内第一个就是su_home_s结构体对象。然后context结构体内包含了其他的对象。要求就是必须确保context结构体内第一个对象一定时su_home_s对象。

2、然后就是调用创建函数创建、初始化context结构体对象。

struct context *ctx = su_home_new(sizeof (struct context));

3、接着使用su_zalloc函数处理内存分配事宜。文字中没明确指出zeroblock变量的类型,但我猜这应该是su_block_s结构体类型。

zeroblock = su_zalloc(ctx->ctx_home, sizeof (*zeroblock));
同时指出,调用su_home_unref后会将su_home_s结构体以及所有由su_home_s结构体分配的su_block_s结构体都释放掉。




su_home_new函数还算简单。我只是对于输入参数size的值有些疑问。首先,它必须大于su_home_t结构体的大小。然而,又将申请到的空间地址赋给home指针。

void *su_home_new(isize_t size)
{
  su_home_t *home;

  assert(size >= sizeof (*home));

  if (size < sizeof (*home))
    return (void)(errno = EINVAL), NULL;
  else if (size > INT_MAX)
    return (void)(errno = ENOMEM), NULL;

  home = calloc(1, size);
  if (home) {
    home->suh_size = (int)size;
    home->suh_blocks = su_hash_alloc(SUB_N);
    if (home->suh_blocks)
      home->suh_blocks->sub_hauto = 0;
    else
      safefree(home), home = NULL;
  }

  return home;
}
如果这个size值是4096,那么让home指向这么一个4K大小的空间是何用意。

你可能感兴趣的:(sofia,sip)