上一篇:系统底层源码分析(11)——alloc、init、new
下一篇:系统底层源码分析(13)——alloc如何关联类
接着上一篇继续看,上一篇提到calloc,但是calloc得要在另一份源码中查看。
- 从libmalloc源码继续探究:
void *
calloc(size_t num_items, size_t size)
{
void *retval;
retval = malloc_zone_calloc(default_zone, num_items, size);
if (retval == NULL) {
errno = ENOMEM;
}
return retval;
}
void *
malloc_zone_calloc(malloc_zone_t *zone, size_t num_items, size_t size)
{
...
ptr = zone->calloc(zone, num_items, size);//控制台打印函数地址,可以得到default_zone_calloc
...
return ptr;
}
- 来到
zone->calloc,如果继续点进去就会进入循环。此时我们应该调用calloc函数,运行打印一下zone->calloc的地址就可以看到真正的调用函数:

default_zone_calloc
static void *
default_zone_calloc(malloc_zone_t *zone, size_t num_items, size_t size)
{
zone = runtime_default_zone();
return zone->calloc(zone, num_items, size);////控制台打印函数地址,可以得到nano_calloc
}
- 然后这里的
zone->calloc也一样打印:

nano_calloc
static void *
nano_calloc(nanozone_t *nanozone, size_t num_items, size_t size)
{
...
if (total_bytes <= NANO_MAX_SIZE) {
void *p = _nano_malloc_check_clear(nanozone, total_bytes, 1);
if (p) {
return p;
} else {
/* FALLTHROUGH to helper zone */
}
}
malloc_zone_t *zone = (malloc_zone_t *)(nanozone->helper_zone);
return zone->calloc(zone, 1, total_bytes);
}
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); // Note slot_key is set here
mag_index_t mag_index = nano_mag_index(nanozone);
nano_meta_admin_t pMeta = &(nanozone->meta_data[mag_index][slot_key]);
ptr = OSAtomicDequeue(&(pMeta->slot_LIFO), offsetof(struct chained_block_s, next));
...
return ptr;
}
- 最后我们来到
_nano_malloc_check_clear,代码很长,我们最需要看关于内存大小的地方:
#define SHIFT_NANO_QUANTUM 4
#define NANO_REGIME_QUANTA_SIZE (1 << SHIFT_NANO_QUANTUM) // 16
static MALLOC_INLINE size_t
segregated_size_to_fit(nanozone_t *nanozone, size_t size, size_t *pKey)
{
size_t k, slot_bytes;
if (0 == size) {
size = NANO_REGIME_QUANTA_SIZE; // Historical behavior
}
//位运算 字节对齐
k = (size + NANO_REGIME_QUANTA_SIZE - 1) >> SHIFT_NANO_QUANTUM; // round up and shift for number of quanta
slot_bytes = k << SHIFT_NANO_QUANTUM; // multiply by power of two quanta size
*pKey = k - 1; // Zero-based!
return slot_bytes;
}
最终开辟的内存空间大小由于字节对齐,可能和申请的不一样。
-
字节对齐
1:数据成员对齐规则:结构(
struct)(或联合(union))的数据成员,第
个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要
从该成员大小或者成员的子成员大小(只要该成员有子成员,比如说是数组,
结构体等)的整数倍开始(比如int为4字节,则要从4的整数倍地址开始存储。2:结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从
其内部最大元素大小的整数倍地址开始存储。(struct a里存有struct b,b
里有char,int,double等元素,那b应该从8的整数倍开始存储。)3:收尾工作:结构体的总大小,也就是
sizeof的结果, 必须是其内部最大
成员的整数倍,不足的要补齐。
举个例子:

这里的内部最大元素是double,所以内存大小为8的整数倍:
Person1的age字节大小为4,由于字节对齐,加4补全为8。
Person1的height字节大小为8,不用补。
Person1的name字节大小为1,由于字节对齐,加7补全为8。
所以Person1总内存大小为24。Person2的age字节大小为4,而后面紧接着name,不足8,先不补。
Person2的name字节大小为1,前面紧接着age,所以加3,一起补全为8。
Person2的height字节大小为8,不用补。
所以Person1总内存大小为16。

图解