美文网首页
系统底层源码分析(12)——alloc如何开辟内存空间

系统底层源码分析(12)——alloc如何开辟内存空间

作者: 无悔zero | 来源:发表于2021-06-12 12:37 被阅读0次

    上一篇:系统底层源码分析(11)——alloc、init、new
    下一篇:系统底层源码分析(13)——alloc如何关联类

    接着上一篇继续看,上一篇提到calloc,但是calloc得要在另一份源码中查看。

    1. 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;
    }
    
    1. 来到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
    }
    
    1. 然后这里的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;
    }
    
    1. 最后我们来到_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))的数据成员,第
    个数据成员放在offset0的地方,以后每个数据成员存储的起始位置要
    从该成员大小或者成员的子成员大小(只要该成员有子成员,比如说是数组,
    结构体等)的整数倍开始(比如int4字节,则要从4的整数倍地址开始存储。

    2:结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从
    其内部最大元素大小的整数倍地址开始存储。(struct a里存有struct b,b
    里有char,int, double等元素,那b应该从8的整数倍开始存储。)

    3:收尾工作:结构体的总大小,也就是sizeof的结果, 必须是其内部最大
    成员的整数倍,不足的要补齐。

    举个例子:

    这里的内部最大元素是double,所以内存大小为8的整数倍:

    1. Person1age字节大小为4,由于字节对齐,加4补全为8
      Person1height字节大小为8,不用补。
      Person1name字节大小为1,由于字节对齐,加7补全为8
      所以Person1总内存大小为24

    2. Person2age字节大小为4,而后面紧接着name,不足8,先不补。
      Person2name字节大小为1,前面紧接着age,所以加3,一起补全为8
      Person2height字节大小为8,不用补。
      所以Person1总内存大小为16

    图解

    相关文章

      网友评论

          本文标题:系统底层源码分析(12)——alloc如何开辟内存空间

          本文链接:https://www.haomeiwen.com/subject/wpwdeltx.html