📄 mem.c
字号:
/* * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1997-2003 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. *//* $Id: mem.c,v 1.98.2.7.2.5 2004/03/16 05:50:24 marka Exp $ */#include <config.h>#include <stdio.h>#include <stdlib.h>#include <stddef.h>#include <limits.h>#include <isc/magic.h>#include <isc/mem.h>#include <isc/msgs.h>#include <isc/ondestroy.h>#include <isc/string.h>#include <isc/mutex.h>#include <isc/util.h>#ifndef ISC_MEM_DEBUGGING#define ISC_MEM_DEBUGGING 0#endifLIBISC_EXTERNAL_DATA unsigned int isc_mem_debugging = ISC_MEM_DEBUGGING;/* * Define ISC_MEM_USE_INTERNAL_MALLOC=1 to use the internal malloc() * implementation in preference to the system one. The internal malloc() * is very space-efficient, and quite fast on uniprocessor systems. It * performs poorly on multiprocessor machines. */#ifndef ISC_MEM_USE_INTERNAL_MALLOC#define ISC_MEM_USE_INTERNAL_MALLOC 0#endif/* * Constants. */#define DEF_MAX_SIZE 1100#define DEF_MEM_TARGET 4096#define ALIGNMENT_SIZE 8 /* must be a power of 2 */#define NUM_BASIC_BLOCKS 64 /* must be > 1 */#define TABLE_INCREMENT 1024#define DEBUGLIST_COUNT 1024/* * Types. */#if ISC_MEM_TRACKLINEStypedef struct debuglink debuglink_t;struct debuglink { ISC_LINK(debuglink_t) link; const void *ptr[DEBUGLIST_COUNT]; unsigned int size[DEBUGLIST_COUNT]; const char *file[DEBUGLIST_COUNT]; unsigned int line[DEBUGLIST_COUNT]; unsigned int count;};#define FLARG_PASS , file, line#define FLARG , const char *file, int line#else#define FLARG_PASS#define FLARG#endiftypedef struct element element;struct element { element * next;};typedef struct { /* * This structure must be ALIGNMENT_SIZE bytes. */ union { size_t size; char bytes[ALIGNMENT_SIZE]; } u;} size_info;struct stats { unsigned long gets; unsigned long totalgets;#if ISC_MEM_USE_INTERNAL_MALLOC unsigned long blocks; unsigned long freefrags;#endif /* ISC_MEM_USE_INTERNAL_MALLOC */};#define MEM_MAGIC ISC_MAGIC('M', 'e', 'm', 'C')#define VALID_CONTEXT(c) ISC_MAGIC_VALID(c, MEM_MAGIC)#if ISC_MEM_TRACKLINEStypedef ISC_LIST(debuglink_t) debuglist_t;#endifstruct isc_mem { unsigned int magic; isc_ondestroy_t ondestroy; isc_mutex_t lock; isc_memalloc_t memalloc; isc_memfree_t memfree; void * arg; size_t max_size; isc_boolean_t checkfree; struct stats * stats; unsigned int references; size_t quota; size_t total; size_t inuse; size_t maxinuse; size_t hi_water; size_t lo_water; isc_boolean_t hi_called; isc_mem_water_t water; void * water_arg; ISC_LIST(isc_mempool_t) pools;#if ISC_MEM_USE_INTERNAL_MALLOC size_t mem_target; element ** freelists; element * basic_blocks; unsigned char ** basic_table; unsigned int basic_table_count; unsigned int basic_table_size; unsigned char * lowest; unsigned char * highest;#endif /* ISC_MEM_USE_INTERNAL_MALLOC */#if ISC_MEM_TRACKLINES debuglist_t * debuglist;#endif unsigned int memalloc_failures;};#define MEMPOOL_MAGIC ISC_MAGIC('M', 'E', 'M', 'p')#define VALID_MEMPOOL(c) ISC_MAGIC_VALID(c, MEMPOOL_MAGIC)struct isc_mempool { /* always unlocked */ unsigned int magic; /* magic number */ isc_mutex_t *lock; /* optional lock */ isc_mem_t *mctx; /* our memory context */ /* locked via the memory context's lock */ ISC_LINK(isc_mempool_t) link; /* next pool in this mem context */ /* optionally locked from here down */ element *items; /* low water item list */ size_t size; /* size of each item on this pool */ unsigned int maxalloc; /* max number of items allowed */ unsigned int allocated; /* # of items currently given out */ unsigned int freecount; /* # of items on reserved list */ unsigned int freemax; /* # of items allowed on free list */ unsigned int fillcount; /* # of items to fetch on each fill */ /* Stats only. */ unsigned int gets; /* # of requests to this pool */ /* Debugging only. */#if ISC_MEMPOOL_NAMES char name[16]; /* printed name in stats reports */#endif};/* * Private Inline-able. */#if ! ISC_MEM_TRACKLINES#define ADD_TRACE(a, b, c, d, e)#define DELETE_TRACE(a, b, c, d, e)#else#define ADD_TRACE(a, b, c, d, e) \ do { \ if ((isc_mem_debugging & (ISC_MEM_DEBUGTRACE | \ ISC_MEM_DEBUGRECORD)) != 0 && \ b != NULL) \ add_trace_entry(a, b, c, d, e); \ } while (0)#define DELETE_TRACE(a, b, c, d, e) delete_trace_entry(a, b, c, d, e)static voidprint_active(isc_mem_t *ctx, FILE *out);/* * mctx must be locked. */static inline voidadd_trace_entry(isc_mem_t *mctx, const void *ptr, unsigned int size FLARG){ debuglink_t *dl; unsigned int i; if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0) fprintf(stderr, isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM, ISC_MSG_ADDTRACE, "add %p size %u " "file %s line %u mctx %p\n"), ptr, size, file, line, mctx); if (mctx->debuglist == NULL) return; if (size > mctx->max_size) size = mctx->max_size; dl = ISC_LIST_HEAD(mctx->debuglist[size]); while (dl != NULL) { if (dl->count == DEBUGLIST_COUNT) goto next; for (i = 0; i < DEBUGLIST_COUNT; i++) { if (dl->ptr[i] == NULL) { dl->ptr[i] = ptr; dl->size[i] = size; dl->file[i] = file; dl->line[i] = line; dl->count++; return; } } next: dl = ISC_LIST_NEXT(dl, link); } dl = malloc(sizeof(debuglink_t)); INSIST(dl != NULL); ISC_LINK_INIT(dl, link); for (i = 1; i < DEBUGLIST_COUNT; i++) { dl->ptr[i] = NULL; dl->size[i] = 0; dl->file[i] = NULL; dl->line[i] = 0; } dl->ptr[0] = ptr; dl->size[0] = size; dl->file[0] = file; dl->line[0] = line; dl->count = 1; ISC_LIST_PREPEND(mctx->debuglist[size], dl, link);}static inline voiddelete_trace_entry(isc_mem_t *mctx, const void *ptr, unsigned int size, const char *file, unsigned int line){ debuglink_t *dl; unsigned int i; if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0) fprintf(stderr, isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM, ISC_MSG_DELTRACE, "del %p size %u " "file %s line %u mctx %p\n"), ptr, size, file, line, mctx); if (mctx->debuglist == NULL) return; if (size > mctx->max_size) size = mctx->max_size; dl = ISC_LIST_HEAD(mctx->debuglist[size]); while (dl != NULL) { for (i = 0; i < DEBUGLIST_COUNT; i++) { if (dl->ptr[i] == ptr) { dl->ptr[i] = NULL; dl->size[i] = 0; dl->file[i] = NULL; dl->line[i] = 0; INSIST(dl->count > 0); dl->count--; if (dl->count == 0) { ISC_LIST_UNLINK(mctx->debuglist[size], dl, link); free(dl); } return; } } dl = ISC_LIST_NEXT(dl, link); } /* * If we get here, we didn't find the item on the list. We're * screwed. */ INSIST(dl != NULL);}#endif /* ISC_MEM_TRACKLINES */#if ISC_MEM_USE_INTERNAL_MALLOCstatic inline size_trmsize(size_t size) { /* * round down to ALIGNMENT_SIZE */ return (size & (~(ALIGNMENT_SIZE - 1)));}static inline size_tquantize(size_t size) { /* * Round up the result in order to get a size big * enough to satisfy the request and be aligned on ALIGNMENT_SIZE * byte boundaries. */ if (size == 0) return (ALIGNMENT_SIZE); return ((size + ALIGNMENT_SIZE - 1) & (~(ALIGNMENT_SIZE - 1)));}static inline isc_boolean_tmore_basic_blocks(isc_mem_t *ctx) { void *new; unsigned char *curr, *next; unsigned char *first, *last; unsigned char **table; unsigned int table_size; size_t increment; int i; /* Require: we hold the context lock. */ /* * Did we hit the quota for this context? */ increment = NUM_BASIC_BLOCKS * ctx->mem_target; if (ctx->quota != 0 && ctx->total + increment > ctx->quota) return (ISC_FALSE); INSIST(ctx->basic_table_count <= ctx->basic_table_size); if (ctx->basic_table_count == ctx->basic_table_size) { table_size = ctx->basic_table_size + TABLE_INCREMENT; table = (ctx->memalloc)(ctx->arg, table_size * sizeof(unsigned char *)); if (table == NULL) { ctx->memalloc_failures++; return (ISC_FALSE); } if (ctx->basic_table_size != 0) { memcpy(table, ctx->basic_table, ctx->basic_table_size * sizeof(unsigned char *)); (ctx->memfree)(ctx->arg, ctx->basic_table); } ctx->basic_table = table; ctx->basic_table_size = table_size; } new = (ctx->memalloc)(ctx->arg, NUM_BASIC_BLOCKS * ctx->mem_target); if (new == NULL) { ctx->memalloc_failures++; return (ISC_FALSE); } ctx->total += increment; ctx->basic_table[ctx->basic_table_count] = new; ctx->basic_table_count++; curr = new; next = curr + ctx->mem_target; for (i = 0; i < (NUM_BASIC_BLOCKS - 1); i++) { ((element *)curr)->next = (element *)next; curr = next; next += ctx->mem_target; } /* * curr is now pointing at the last block in the * array. */ ((element *)curr)->next = NULL; first = new; last = first + NUM_BASIC_BLOCKS * ctx->mem_target - 1; if (first < ctx->lowest || ctx->lowest == NULL) ctx->lowest = first; if (last > ctx->highest) ctx->highest = last; ctx->basic_blocks = new; return (ISC_TRUE);}static inline isc_boolean_tmore_frags(isc_mem_t *ctx, size_t new_size) { int i, frags; size_t total_size; void *new; unsigned char *curr, *next; /* * Try to get more fragments by chopping up a basic block. */ if (ctx->basic_blocks == NULL) { if (!more_basic_blocks(ctx)) { /* * We can't get more memory from the OS, or we've * hit the quota for this context. */ /* * XXXRTH "At quota" notification here. */ return (ISC_FALSE); } } total_size = ctx->mem_target; new = ctx->basic_blocks; ctx->basic_blocks = ctx->basic_blocks->next; frags = total_size / new_size; ctx->stats[new_size].blocks++; ctx->stats[new_size].freefrags += frags; /* * Set up a linked-list of blocks of size * "new_size". */ curr = new; next = curr + new_size; total_size -= new_size; for (i = 0; i < (frags - 1); i++) { ((element *)curr)->next = (element *)next; curr = next; next += new_size; total_size -= new_size; } /* * Add the remaining fragment of the basic block to a free list. */ total_size = rmsize(total_size); if (total_size > 0) { ((element *)next)->next = ctx->freelists[total_size]; ctx->freelists[total_size] = (element *)next; ctx->stats[total_size].freefrags++; } /* * curr is now pointing at the last block in the * array. */ ((element *)curr)->next = NULL; ctx->freelists[new_size] = new; return (ISC_TRUE);}static inline void *mem_getunlocked(isc_mem_t *ctx, size_t size) { size_t new_size = quantize(size); void *ret; if (size >= ctx->max_size || new_size >= ctx->max_size) { /* * memget() was called on something beyond our upper limit. */ if (ctx->quota != 0 && ctx->total + size > ctx->quota) { ret = NULL; goto done; } ret = (ctx->memalloc)(ctx->arg, size); if (ret == NULL) { ctx->memalloc_failures++; goto done; } ctx->total += size; ctx->inuse += size; ctx->stats[ctx->max_size].gets++; ctx->stats[ctx->max_size].totalgets++; /* * If we don't set new_size to size, then the * ISC_MEM_FILL code might write over bytes we * don't own. */ new_size = size; goto done; } /* * If there are no blocks in the free list for this size, get a chunk * of memory and then break it up into "new_size"-sized blocks, adding * them to the free list. */ if (ctx->freelists[new_size] == NULL && !more_frags(ctx, new_size)) return (NULL); /* * The free list uses the "rounded-up" size "new_size". */ ret = ctx->freelists[new_size]; ctx->freelists[new_size] = ctx->freelists[new_size]->next; /* * The stats[] uses the _actual_ "size" requested by the * caller, with the caveat (in the code above) that "size" >= the * max. size (max_size) ends up getting recorded as a call to * max_size. */ ctx->stats[size].gets++; ctx->stats[size].totalgets++; ctx->stats[new_size].freefrags--; ctx->inuse += new_size; done:#if ISC_MEM_FILL if (ret != NULL) memset(ret, 0xbe, new_size); /* Mnemonic for "beef". */#endif return (ret);}#if ISC_MEM_FILL && ISC_MEM_CHECKOVERRUNstatic inline voidcheck_overrun(void *mem, size_t size, size_t new_size) { unsigned char *cp; cp = (unsigned char *)mem; cp += size; while (size < new_size) { INSIST(*cp == 0xbe); cp++; size++; }}#endifstatic inline voidmem_putunlocked(isc_mem_t *ctx, void *mem, size_t size) { size_t new_size = quantize(size); if (size == ctx->max_size || new_size >= ctx->max_size) { /* * memput() called on something beyond our upper limit. */#if ISC_MEM_FILL memset(mem, 0xde, size); /* Mnemonic for "dead". */#endif (ctx->memfree)(ctx->arg, mem); INSIST(ctx->stats[ctx->max_size].gets != 0); ctx->stats[ctx->max_size].gets--; INSIST(size <= ctx->total); ctx->inuse -= size; ctx->total -= size; return; }#if ISC_MEM_FILL#if ISC_MEM_CHECKOVERRUN check_overrun(mem, size, new_size);#endif memset(mem, 0xde, new_size); /* Mnemonic for "dead". */#endif /* * The free list uses the "rounded-up" size "new_size". */ ((element *)mem)->next = ctx->freelists[new_size]; ctx->freelists[new_size] = (element *)mem; /* * The stats[] uses the _actual_ "size" requested by the * caller, with the caveat (in the code above) that "size" >= the * max. size (max_size) ends up getting recorded as a call to * max_size. */ INSIST(ctx->stats[size].gets != 0); ctx->stats[size].gets--; ctx->stats[new_size].freefrags++; ctx->inuse -= new_size;}#else /* ISC_MEM_USE_INTERNAL_MALLOC */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -