FreeBSD kernel malloc

FreeBSD kernal malloc 是通过 zone allocaotor 实现的。其基本思想就是,创建一些较小内存的 zones ,以供小内存分配;在分配较大内存时,使用 uma_large_malloc 。
kmeminit 进行一些初始化动作,包括初始化那些存储较小内存的 zones 。
/* 
 * Small malloc(9) memory allocations are allocated from a set of UMA buckets 
 * of various sizes. 
 * 
 * XXX: The comment here used to read "These won't be powers of two for 
 * long."  It's possible that a significant amount of wasted memory could be 
 * recovered by tuning the sizes of these buckets. 
 */  
struct {  
    int kz_size;  
    char *kz_name;  
    uma_zone_t kz_zone;  
} kmemzones[] = {  
    {16, "16", NULL},  
    {32, "32", NULL},  
    {64, "64", NULL},  
    {128, "128", NULL},  
    {256, "256", NULL},  
    {512, "512", NULL},  
    {1024, "1024", NULL},  
    {2048, "2048", NULL},  
    {4096, "4096", NULL},  
#if PAGE_SIZE > 4096  
    {8192, "8192", NULL},  
#if PAGE_SIZE > 8192  
    {16384, "16384", NULL},  
#if PAGE_SIZE > 16384  
    {32768, "32768", NULL},  
#if PAGE_SIZE > 32768  
    {65536, "65536", NULL},  
#if PAGE_SIZE > 65536  
#error  "Unsupported PAGE_SIZE"  
#endif  /* 65536 */  
#endif  /* 32768 */  
#endif  /* 16384 */  
#endif  /* 8192 */  
#endif  /* 4096 */  
    {0, NULL},  
};

    for (i = 0, indx = 0; kmemzones[indx].kz_size != 0; indx++) {  
        int size = kmemzones[indx].kz_size;  
        char *name = kmemzones[indx].kz_name;  
        kmemzones[indx].kz_zone = uma_zcreate(name, size,  
#ifdef INVARIANTS  
            mtrash_ctor, mtrash_dtor, mtrash_init, mtrash_fini,  
#else  
            NULL, NULL, NULL, NULL,  
#endif  
            UMA_ALIGN_PTR, UMA_ZONE_MALLOC);  
              
        for (;i <= size; i+= KMEM_ZBASE)  
            kmemsize[i >> KMEM_ZSHIFT] = indx;  
          
    }

void *malloc(unsigned long size, struct malloc_type *mtp, int flags); 分配内存时,需要一个 struct malloc_type 类型的参数,该实参保存了分配信息。malloc_init 和 malloc_uninit 构造和析够一个 "struct malloc_type",malloc_uninit 检查是否存在内存泄漏。
void  
malloc_init(void *data)  
{  
    struct malloc_type_internal *mtip;  
    struct malloc_type *mtp;  
    KASSERT(cnt.v_page_count != 0, ("malloc_register before vm_init"));  
    mtp = data;  
    mtip = uma_zalloc(mt_zone, M_WAITOK | M_ZERO);  
    mtp->ks_handle = mtip;  
    mtx_lock(&malloc_mtx);  
    mtp->ks_next = kmemstatistics;  
    kmemstatistics = mtp;  
    kmemcount++;  
    mtx_unlock(&malloc_mtx);  
}  
void  
malloc_uninit(void *data)  
{  
    struct malloc_type_internal *mtip;  
    struct malloc_type_stats *mtsp;  
    struct malloc_type *mtp, *temp;  
    uma_slab_t slab;  
    long temp_allocs, temp_bytes;  
    int i;  
    mtp = data;  
    KASSERT(mtp->ks_handle != NULL, ("malloc_deregister: cookie NULL"));  
    mtx_lock(&malloc_mtx);  
    mtip = mtp->ks_handle;  
    mtp->ks_handle = NULL;  
    if (mtp != kmemstatistics) {  
        for (temp = kmemstatistics; temp != NULL;  
            temp = temp->ks_next) {  
            if (temp->ks_next == mtp)  
                temp->ks_next = mtp->ks_next;  
        }  
    } else  
        kmemstatistics = mtp->ks_next;  
    kmemcount--;  
    mtx_unlock(&malloc_mtx);  
    /* 
     * Look for memory leaks. 
     */  
    temp_allocs = temp_bytes = 0;  
    for (i = 0; i < MAXCPU; i++) {  
        mtsp = &mtip->mti_stats[i];  
        temp_allocs += mtsp->mts_numallocs;  
        temp_allocs -= mtsp->mts_numfrees;  
        temp_bytes += mtsp->mts_memalloced;  
        temp_bytes -= mtsp->mts_memfreed;  
    }  
    if (temp_allocs > 0 || temp_bytes > 0) {  
        printf("Warning: memory type %s leaked memory on destroy "  
            "(%ld allocations, %ld bytes leaked).\n", mtp->ks_shortdesc,  
            temp_allocs, temp_bytes);  
    }  
    slab = vtoslab((vm_offset_t) mtip & (~UMA_SLAB_MASK));  
    uma_zfree_arg(mt_zone, mtip, slab);  
}

malloc 根据要分配的内存大小,决定从 kmemzones 中创建的 zones 分配,还是通过 uma_large_malloc 分配。free 根据 slab 标志,得出 addr 是如何分配的,并以对应的方式释放之。
/* 
 * An allocation has succeeded -- update malloc type statistics for the 
 * amount of bucket size.  Occurs within a critical section so that the 
 * thread isn't preempted and doesn't migrate while updating per-PCU 
 * statistics. 
 */  
static void  
malloc_type_zone_allocated(struct malloc_type *mtp, unsigned long size,  
    int zindx)  
{  
    struct malloc_type_internal *mtip;  
    struct malloc_type_stats *mtsp;  
    critical_enter();  
    mtip = mtp->ks_handle;  
    mtsp = &mtip->mti_stats[curcpu];  
    if (size > 0) {  
        mtsp->mts_memalloced += size;  
        mtsp->mts_numallocs++;  
    }  
    if (zindx != -1)  
        mtsp->mts_size |= 1 << zindx;  
#ifdef KDTRACE_HOOKS  
    if (dtrace_malloc_probe != NULL) {  
        uint32_t probe_id = mtip->mti_probes[DTMALLOC_PROBE_MALLOC];  
        if (probe_id != 0)  
            (dtrace_malloc_probe)(probe_id,  
                (uintptr_t) mtp, (uintptr_t) mtip,  
                (uintptr_t) mtsp, size, zindx);  
    }  
#endif  
    critical_exit();  
}  
void  
malloc_type_allocated(struct malloc_type *mtp, unsigned long size)  
{  
    if (size > 0)  
        malloc_type_zone_allocated(mtp, size, -1);  
}  
/* 
 * A free operation has occurred -- update malloc type statistics for the 
 * amount of the bucket size.  Occurs within a critical section so that the 
 * thread isn't preempted and doesn't migrate while updating per-CPU 
 * statistics. 
 */  
void  
malloc_type_freed(struct malloc_type *mtp, unsigned long size)  
{  
    struct malloc_type_internal *mtip;  
    struct malloc_type_stats *mtsp;  
    critical_enter();  
    mtip = mtp->ks_handle;  
    mtsp = &mtip->mti_stats[curcpu];  
    mtsp->mts_memfreed += size;  
    mtsp->mts_numfrees++;  
#ifdef KDTRACE_HOOKS  
    if (dtrace_malloc_probe != NULL) {  
        uint32_t probe_id = mtip->mti_probes[DTMALLOC_PROBE_FREE];  
        if (probe_id != 0)  
            (dtrace_malloc_probe)(probe_id,  
                (uintptr_t) mtp, (uintptr_t) mtip,  
                (uintptr_t) mtsp, size, 0);  
    }  
#endif  
    critical_exit();  
}

/* 
 *  malloc: 
 * 
 *  Allocate a block of memory. 
 * 
 *  If M_NOWAIT is set, this routine will not block and return NULL if 
 *  the allocation fails. 
 */  
void *  
malloc(unsigned long size, struct malloc_type *mtp, int flags)  
{  
    int indx;  
    caddr_t va;  
    uma_zone_t zone;  
    uma_keg_t keg;  
#if defined(DIAGNOSTIC) || defined(DEBUG_REDZONE)  
    unsigned long osize = size;  
#endif  
#ifdef INVARIANTS  
    /* 
     * Check that exactly one of M_WAITOK or M_NOWAIT is specified. 
     */  
    indx = flags & (M_WAITOK | M_NOWAIT);  
    if (indx != M_NOWAIT && indx != M_WAITOK) {  
        static  struct timeval lasterr;  
        static  int curerr, once;  
        if (once == 0 && ppsratecheck(&lasterr, &curerr, 1)) {  
            printf("Bad malloc flags: %x\n", indx);  
            kdb_backtrace();  
            flags |= M_WAITOK;  
            once++;  
        }  
    }  
#endif  
#ifdef MALLOC_MAKE_FAILURES  
    if ((flags & M_NOWAIT) && (malloc_failure_rate != 0)) {  
        atomic_add_int(&malloc_nowait_count, 1);  
        if ((malloc_nowait_count % malloc_failure_rate) == 0) {  
            atomic_add_int(&malloc_failure_count, 1);  
            t_malloc_fail = time_uptime;  
            return (NULL);  
        }  
    }  
#endif  
    if (flags & M_WAITOK)  
        KASSERT(curthread->td_intr_nesting_level == 0,  
           ("malloc(M_WAITOK) in interrupt context"));  
#ifdef DEBUG_MEMGUARD  
    if (memguard_cmp(mtp))  
        return memguard_alloc(size, flags);  
#endif  
#ifdef DEBUG_REDZONE  
    size = redzone_size_ntor(size);  
#endif  
    if (size <= KMEM_ZMAX) {  
        if (size & KMEM_ZMASK)  
            size = (size & ~KMEM_ZMASK) + KMEM_ZBASE;  
        indx = kmemsize[size >> KMEM_ZSHIFT];  
        zone = kmemzones[indx].kz_zone;  
        keg = zone->uz_keg;  
#ifdef MALLOC_PROFILE  
        krequests[size >> KMEM_ZSHIFT]++;  
#endif  
        va = uma_zalloc(zone, flags);  
        if (va != NULL)  
            size = keg->uk_size;  
        malloc_type_zone_allocated(mtp, va == NULL ? 0 : size, indx);  
    } else {  
        size = roundup(size, PAGE_SIZE);  
        zone = NULL;  
        keg = NULL;  
        va = uma_large_malloc(size, flags);  
        malloc_type_allocated(mtp, va == NULL ? 0 : size);  
    }  
    if (flags & M_WAITOK)  
        KASSERT(va != NULL, ("malloc(M_WAITOK) returned NULL"));  
    else if (va == NULL)  
        t_malloc_fail = time_uptime;  
#ifdef DIAGNOSTIC  
    if (va != NULL && !(flags & M_ZERO)) {  
        memset(va, 0x70, osize);  
    }  
#endif  
#ifdef DEBUG_REDZONE  
    if (va != NULL)  
        va = redzone_setup(va, osize);  
#endif  
    return ((void *) va);  
}  
/* 
 *  free: 
 * 
 *  Free a block of memory allocated by malloc. 
 * 
 *  This routine may not block. 
 */  
void  
free(void *addr, struct malloc_type *mtp)  
{  
    uma_slab_t slab;  
    u_long size;  
    /* free(NULL, ...) does nothing */  
    if (addr == NULL)  
        return;  
#ifdef DEBUG_MEMGUARD  
    if (memguard_cmp(mtp)) {  
        memguard_free(addr);  
        return;  
    }  
#endif  
#ifdef DEBUG_REDZONE  
    redzone_check(addr);  
    addr = redzone_addr_ntor(addr);  
#endif  
    size = 0;  
    slab = vtoslab((vm_offset_t)addr & (~UMA_SLAB_MASK));  
    if (slab == NULL)  
        panic("free: address %p(%p) has not been allocated.\n",  
            addr, (void *)((u_long)addr & (~UMA_SLAB_MASK)));  
    if (!(slab->us_flags & UMA_SLAB_MALLOC)) {  
#ifdef INVARIANTS  
        struct malloc_type **mtpp = addr;  
#endif  
        size = slab->us_keg->uk_size;  
#ifdef INVARIANTS  
        /* 
         * Cache a pointer to the malloc_type that most recently freed 
         * this memory here.  This way we know who is most likely to 
         * have stepped on it later. 
         * 
         * This code assumes that size is a multiple of 8 bytes for 
         * 64 bit machines 
         */  
        mtpp = (struct malloc_type **)  
            ((unsigned long)mtpp & ~UMA_ALIGN_PTR);  
        mtpp += (size - sizeof(struct malloc_type *)) /  
            sizeof(struct malloc_type *);  
        *mtpp = mtp;  
#endif  
        uma_zfree_arg(LIST_FIRST(&slab->us_keg->uk_zones), addr, slab);  
    } else {  
        size = slab->us_size;  
        uma_large_free(slab);  
    }  
    malloc_type_freed(mtp, size);  
}

realloc  根据新大小和旧大小的大小关系,决定是分配新的内存还是重用旧的内存。
/* 
 *  realloc: change the size of a memory block 
 */  
void *  
realloc(void *addr, unsigned long size, struct malloc_type *mtp, int flags)  
{  
    uma_slab_t slab;  
    unsigned long alloc;  
    void *newaddr;  
    /* realloc(NULL, ...) is equivalent to malloc(...) */  
    if (addr == NULL)  
        return (malloc(size, mtp, flags));  
    /* 
     * XXX: Should report free of old memory and alloc of new memory to 
     * per-CPU stats. 
     */  
#ifdef DEBUG_MEMGUARD  
if (memguard_cmp(mtp)) {  
    slab = NULL;  
    alloc = size;  
} else {  
#endif  
#ifdef DEBUG_REDZONE  
    slab = NULL;  
    alloc = redzone_get_size(addr);  
#else  
    slab = vtoslab((vm_offset_t)addr & ~(UMA_SLAB_MASK));  
    /* Sanity check */  
    KASSERT(slab != NULL,  
        ("realloc: address %p out of range", (void *)addr));  
    /* Get the size of the original block */  
    if (!(slab->us_flags & UMA_SLAB_MALLOC))  
        alloc = slab->us_keg->uk_size;  
    else  
        alloc = slab->us_size;  
    /* Reuse the original block if appropriate */  
    if (size <= alloc  
        && (size > (alloc >> REALLOC_FRACTION) || alloc == MINALLOCSIZE))  
        return (addr);  
#endif /* !DEBUG_REDZONE */  
#ifdef DEBUG_MEMGUARD  
}  
#endif  
    /* Allocate a new, bigger (or smaller) block */  
    if ((newaddr = malloc(size, mtp, flags)) == NULL)  
        return (NULL);  
    /* Copy over original contents */  
    bcopy(addr, newaddr, min(size, alloc));  
    free(addr, mtp);  
    return (newaddr);  
}  
/* 
 *  reallocf: same as realloc() but free memory on failure. 
 */  
void *  
reallocf(void *addr, unsigned long size, struct malloc_type *mtp, int flags)  
{  
    void *mem;  
    if ((mem = realloc(addr, size, mtp, flags)) == NULL)  
        free(addr, mtp);  
    return (mem);  
}

参考文章:
    * FreeBSD malloc(9) manual page

你可能感兴趣的:(thread,C++,c,C#,FreeBSD)