1. 探索libmalloc源码

402 阅读3分钟

1. 进入 calloc 函数

int main(int argc, const char * argv[]) {
	@autoreleasepool {
             int *array = calloc(1, sizeof(int));
             array[0] = 10;
             printf("array[0] = %d\n",array[0]);
             free(array);
             array = NULL;   
	}
	return 0;
}
// calloc方法实现
void *
calloc(size_t num_items, size_t size)
{
    return _malloc_zone_calloc(default_zone, num_items, size, MZ_POSIX);
}

default_zone 是一个 malloc_zone_t* 结构体指针里边有一个 _malloc_zone_t类型结构体变量 很多跟内存操作的方法都跟 _malloc_zone_t有关.

_malloc_zone_t 是结构体变量 内部有很多分配内存的操作函数, 比如 malloc calloc free size 等函数指针变量

typedef struct _malloc_zone_t {
    size_t 	(* MALLOC_ZONE_FN_PTR(size))(struct _malloc_zone_t *zone, const void *ptr); // 对应default_zone_size函数
    void 	*(* MALLOC_ZONE_FN_PTR(malloc))(struct _malloc_zone_t *zone, size_t size); // 对应default_zone_malloc函数
    void 	*(* MALLOC_ZONE_FN_PTR(calloc))(struct _malloc_zone_t *zone, size_t num_items, size_t size); // 对应default_zone_calloc函数
    void 	*(* MALLOC_ZONE_FN_PTR(valloc))(struct _malloc_zone_t *zone, size_t size); // 对应default_zone_valloc函数
    void 	(* MALLOC_ZONE_FN_PTR(free))(struct _malloc_zone_t *zone, void *ptr); // 对应 default_zone_free函数
    void 	*(* MALLOC_ZONE_FN_PTR(realloc))(struct _malloc_zone_t *zone, void *ptr, size_t size); //对应default_zone_realloc函数
    void 	(* MALLOC_ZONE_FN_PTR(destroy))(struct _malloc_zone_t *zone); //对应default_zone_destroy函数
    const char	*zone_name; //内存空间的名词
		........  不再一一列举 ..........
} malloc_zone_t;

_malloc_zone_t 内部的函数指针变量指向的就是下边的函数 它们的内存地址是一样的例如 zone->calloc == default_zone_calloc, 意味着真正分配内存的方法 就这下边的函数里

__attribute__((section("__DATA,__v_zone")))
__attribute__((aligned(PAGE_MAX_SIZE))) = {
	NULL,
	NULL,
	default_zone_size,  // zone->size(zone) 函数指针调用的就是这个函数
	default_zone_malloc, // zone->malloc(zone, size); 函数指针调用的就是这个函数
	default_zone_calloc, // zone->calloc(zone, num_items, size); 函数指针调用的就是这个函数
	default_zone_valloc, // zone->valloc(default_zone,10); 函数指针调用的就是这个函数
	default_zone_free, // zone->free(default_zone,ptr); 函数指针调用的就是这个函数
	default_zone_realloc, // zone->realloc()  函数指针调用的就是这个函数
	default_zone_destroy, // zone->destroy()  函数指针调用的就是这个函数
  ...... 不再一一列举其他.......
};

2. 进入_malloc_zone_calloc函数

MALLOC_NOINLINE
static void *
_malloc_zone_calloc(malloc_zone_t *zone, size_t num_items, size_t size,
		malloc_zone_options_t mzo)
{
	MALLOC_TRACE(TRACE_calloc | DBG_FUNC_START, (uintptr_t)zone, num_items, size, 0);

	void *ptr;
	if (malloc_check_start) {
		internal_check();
	}
	ptr = zone->calloc(zone, num_items, size); // 核心代码分配内存空间并返回指向内存空间的指针地址,  zone->calloc函数指针对应的函数实现在 default_zone_calloc中

	if (os_unlikely(malloc_logger)) {
		malloc_logger(MALLOC_LOG_TYPE_ALLOCATE | MALLOC_LOG_TYPE_HAS_ZONE | MALLOC_LOG_TYPE_CLEARED, (uintptr_t)zone,
				(uintptr_t)(num_items * size), 0, (uintptr_t)ptr, 0);
	}

	MALLOC_TRACE(TRACE_calloc | DBG_FUNC_END, (uintptr_t)zone, num_items, size, (uintptr_t)ptr);
	if (os_unlikely(ptr == NULL)) {
		malloc_set_errno_fast(mzo, ENOMEM);
	}
	return ptr;
}

3. 进入 default_zone_calloc函数

static void *
default_zone_calloc(malloc_zone_t *zone, size_t num_items, size_t size)
{
	zone = runtime_default_zone(); // 内部调用inline_malloc_default_zone函数 拿到 malloc_zones[0] 的值
	# 内部会调用  nano_calloc函数 因为 在初始化时 nanozone->basic_zone.calloc = (void *)nano_calloc; zone->calloc的值就是nano_calloc函数内存地址
	return zone->calloc(zone, num_items, size);
}

4. 进入 nano_calloc 函数

static void *
nano_calloc(nanozone_t *nanozone, size_t num_items, size_t size)
{
	size_t total_bytes;

	if (calloc_get_size(num_items, size, 0, &total_bytes)) {
		return NULL;
	}

	if (total_bytes <= NANO_MAX_SIZE) { // 分配内存小于==256 NANO_MAX_SIZE是一个宏定义默认就是 256 快速分配内存的方式 分配的内存是 16 的倍数要对内存对齐
		void *p = _nano_malloc_check_clear(nanozone, total_bytes, 1);
		if (p) {
			return p;
		} else {
			/* FALLTHROUGH to helper zone */
		}
	}
  // 大于 256 字节内存 会采用helper_zone.calloc去分配内存 具体函数 szone_malloc_should_clear()
	malloc_zone_t *zone = (malloc_zone_t *)(nanozone->helper_zone);
	return zone->calloc(zone, 1, total_bytes);
}

5. 进入 _nano_malloc_check_clear 函数

segregated_size_to_fit 计算内存大小
static void *
_nano_malloc_check_clear(nanozone_t *nanozone, size_t size, boolean_t cleared_requested)
{
	MALLOC_TRACE(TRACE_nano_malloc, (uintptr_t)nanozone, size, cleared_requested, 0);

	void *ptr;
	size_t slot_key;
	size_t slot_bytes = segregated_size_to_fit(nanozone, size, &slot_key); // 计算要内存大小 为 16 倍数
	mag_index_t mag_index = nano_mag_index(nanozone);
	printf("slot_key -> %zu\n",slot_key);
	nano_meta_admin_t pMeta = &(nanozone->meta_data[mag_index][slot_key]);
	printf("pMeta->slot_bytes -> : %d\n",pMeta->slot_bytes);
	ptr = OSAtomicDequeue(&(pMeta->slot_LIFO), offsetof(struct chained_block_s, next));
	if (ptr) {
		......
	} else {
    /// ------------ 核心逻辑 ----
		ptr = segregated_next_block(nanozone, pMeta, slot_bytes, mag_index);
	}

	if (cleared_requested && ptr) {
		memset(ptr, 0, slot_bytes); // TODO: Needs a memory barrier after memset to ensure zeroes land first?
	}
	return ptr;
}

6. 进入 segregated_size_to_fit函数

static MALLOC_INLINE size_t
segregated_size_to_fit(nanozone_t *nanozone, size_t size, size_t *pKey)
{
	size_t k, slot_bytes;

	/*
	// 如果 size 传 0 通过 1 << 4 得到 16  1左移四位

	1	0000  0000  0000  0001

		0000  0000  0001 0000
	*/
	if (0 == size) {
		size = NANO_REGIME_QUANTA_SIZE; // Historical behavior 历史行为 默认最少分配 16 个字节内存大小
	}
	k = (size + NANO_REGIME_QUANTA_SIZE - 1) >> SHIFT_NANO_QUANTUM; // 对量子数进行四舍五入和移位
	slot_bytes = k << SHIFT_NANO_QUANTUM;							// 乘以两个量子大小的幂
	*pKey = k - 1;													// Zero-based!
	printf("slot_bytes -> %zu\n",slot_bytes);
	return slot_bytes;
}

7. 进入segregated_next_block函数

static MALLOC_INLINE void *
segregated_next_block(nanozone_t *nanozone, nano_meta_admin_t pMeta, size_t slot_bytes, unsigned int mag_index)
{
    while (1) {
        //当前这块pMeta可用内存的结束地址
        uintptr_t theLimit = pMeta->slot_limit_addr; // Capture the slot limit that bounds slot_bump_addr right now
        //原子的为pMeta->slot_bump_addr添加slot_bytes的长度,偏移到下一个地址
        uintptr_t b = OSAtomicAdd64Barrier(slot_bytes, (volatile int64_t *)&(pMeta->slot_bump_addr));
        //减去添加的偏移量,获取当前可以获取的地址
        b -= slot_bytes; // Atomic op returned addr of *next* free block. Subtract to get addr for *this* allocation.
        
        if (b < theLimit) {   // Did we stay within the bound of the present slot allocation?
            //如果地址还在范围之内,则返回地址
            return (void *)b; // Yep, so the slot_bump_addr this thread incremented is good to go
        } else {
            //已经用尽了
            if (pMeta->slot_exhausted) { // exhausted all the bands availble for this slot?
                pMeta->slot_bump_addr = theLimit;
                return 0;                 // We're toast
            } else {
                // One thread will grow the heap, others will see its been grown and retry allocation
                _malloc_lock_lock(&nanozone->band_resupply_lock[mag_index]);
                // re-check state now that we've taken the lock
                //多线程的缘故,重新检查是否用尽
                if (pMeta->slot_exhausted) {
                    _malloc_lock_unlock(&nanozone->band_resupply_lock[mag_index]);
                    return 0; // Toast
                } else if (b < pMeta->slot_limit_addr) {
                    //如果小于最大限制地址,当重新申请一个新的band后,重新尝试while
                    _malloc_lock_unlock(&nanozone->band_resupply_lock[mag_index]);
                    continue; // ... the slot was successfully grown by first-taker (not us). Now try again.
                } else if (segregated_band_grow(nanozone, pMeta, slot_bytes, mag_index)) {
                    //申请新的band成功,重新尝试while
                    _malloc_lock_unlock(&nanozone->band_resupply_lock[mag_index]);
                    continue; // ... the slot has been successfully grown by us. Now try again.
                } else {
                    pMeta->slot_exhausted = TRUE;
                    pMeta->slot_bump_addr = theLimit;
                    _malloc_lock_unlock(&nanozone->band_resupply_lock[mag_index]);
                    return 0;
                }
            }
        }
    }
}

至此整个malloc调用流程全部展示完毕, 最主要的过程就是如何分配内存大小, 内存是如何进行16字节对齐的.