zhxinu 发表于 2013-1-16 17:37:47

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.kz_size != 0; indx++) {          int size = kmemzones.kz_size;          char *name = kmemzones.kz_name;          kmemzones.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 = indx;                }
void *malloc(unsigned long size, struct malloc_type *mtp, int flags); 分配内存时,需要一个 struct malloc_type 类型的参数,该实参保存了分配信息。malloc_init 和 malloc_uninit 构造和析够一个 "struct malloc_type",malloc_uninit 检查是否存在内存泄漏。
voidmalloc_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);}voidmalloc_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;          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 voidmalloc_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;      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;          if (probe_id != 0)            (dtrace_malloc_probe)(probe_id,                  (uintptr_t) mtp, (uintptr_t) mtip,                  (uintptr_t) mtsp, size, zindx);      }#endif      critical_exit();}voidmalloc_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.*/voidmalloc_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;      mtsp->mts_memfreed += size;      mtsp->mts_numfrees++;#ifdef KDTRACE_HOOKS      if (dtrace_malloc_probe != NULL) {          uint32_t probe_id = mtip->mti_probes;          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) {          staticstruct timeval lasterr;          staticint 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;          zone = kmemzones.kz_zone;          keg = zone->uz_keg;#ifdef MALLOC_PROFILE          krequests++;#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.*/voidfree(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_MEMGUARDif (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
页: [1]
查看完整版本: FreeBSD kernel malloc