📄 alloc.c
字号:
/* * Copyright (c) 1994 David I. Bell * Permission is granted to use, distribute, or modify this source, * provided that this copyright notice remains intact. * * Description: * This is a very fast storage allocator. It allocates blocks of a small * number of different sizes, and keeps free lists of each size. Blocks * that don't exactly fit are passed up to the next larger size. In this * implementation, the available sizes are 2^n-4 (or 2^n-12) bytes long. * This is designed for use in a program that uses vast quantities of * memory, but bombs when it runs out. * * Abnormal Conditions * This is a public domain storage allocator. * * Modifications: * Date Programmer Description of modification * 27-FEB-90 Landon Curt Noll most systems can ignore alloc.c * 2-OCT-89 David I. Bell Add free list. Sbrk now optional * 30-JUN-87 Peter Miller Made it work on Slimos. * 21-FEB-82 Chris Kingsley Initial Coding * kingsley@cit-20 Caltech */#include <stdio.h>#include "alloc.h"#include "have_stdlib.h"#ifdef HAVE_STDLIB_H# include <stdlib.h>#endif#if 0#define DEBUG 1 /* defined if debugging code enabled */#define MSTATS 1 /* defined if memory statistics kept */#endif#define NO_SBRK 1 /* defined if cannot use sbrk */#if defined(CALC_MALLOC)/* * Make these functions really accessible here. */#undef malloc#undef realloc#undef free#ifdef DEBUG#define assert(x,v) if ((x)==0) assertfailed(v)#else#define assert(x,v)#endiftypedef unsigned char u_char;typedef unsigned short u_short;typedef unsigned int u_int;typedef char * caddr_t;#ifdef NO_SBRKextern char * malloc();extern char * realloc();#elseextern char * sbrk();#endif/* * The overhead on a block is at least 4 bytes. When free, this space * contains a pointer to the next free block, and the bottom two bits must * be zero. When in use, the first byte is set to MAGIC, and the second * byte is the size index. The remaining bytes are for alignment. * If range checking (RCHECK) is enabled and the size of the block fits * in two bytes, then the top two bytes hold the size of the requested block * plus the range checking words, and the header word MINUS ONE. */union overhead{ union overhead * ov_next; /* when free */ struct { u_char ovu_magic; /* magic number */ u_char ovu_index; /* bucket # */#define ov_magic ovu.ovu_magic#define ov_index ovu.ovu_index#ifdef RCHECK u_short ovu_size; /* actual block size */ u_int ovu_rmagic; /* range magic number */#define ov_size ovu.ovu_size#define ov_rmagic ovu.ovu_rmagic#endif } ovu;};#define QUANTUM_NBITS 4#define QUANTUM (1<<QUANTUM_NBITS)#define MAGIC 0xff /* magic # on accounting info */#define RMAGIC 0x55555555 /* magic # on range info */#ifdef RCHECK#define RSLOP sizeof(u_int)#else#define RSLOP 0#endif/* * nextf[i] is the pointer to the next free block of size 2^(i+3). The * smallest allocatable block is 8 bytes. The overhead information * precedes the data area returned to the user. */#define NBUCKETS 32 /* we can't run out on a 32 bit machine! */static union overhead * nextf[NBUCKETS];static union overhead *watchloc = 0; /* location to be watched */#ifdef MSTATS/* * nmalloc[i] is the difference between the number of mallocs and frees * for a given block size. */static u_int nmalloc[NBUCKETS];#endif/* * Watch some allocated memory to see if it gets blasted. */allocwatch(cp) char *cp;{ if (cp == NULL) { watchloc = NULL; return; } watchloc = (union overhead *)cp - 1; assert(watchloc->ov_magic == MAGIC, 10);}alloccheck(){ assert((watchloc == NULL) || (watchloc->ov_magic == MAGIC), 11);}/* * NAME * morecore - get more memory * * SYNOPSIS * void * morecore(bucket) * int bucket; * * DESCRIPTION * Morecore is used to allocate more memory to the indicated bucket. * * RETURNS * void */static voidmorecore(bucket) register u_int bucket;{ register union overhead * op; register u_int rnu; /* 2^rnu bytes will be requested */ register u_int nblks; /* become nblks blocks of the desired size */ register u_int siz; assert(bucket >= QUANTUM_NBITS, 1); assert(bucket < NBUCKETS, 2); assert(!nextf[bucket], 3);#ifndef NO_SBRK /* * Insure memory is allocated on a page boundary. * Should make getpageize() call? */#define PAGE_SIZE (1<<10) siz = (u_int)sbrk(0); if(siz & (PAGE_SIZE-1)) sbrk(PAGE_SIZE - (siz & (PAGE_SIZE-1)));#endif /* take 2k unless the block is bigger than that */ rnu = (bucket <= 11) ? 11 : bucket; assert(rnu >= bucket, 4); nblks = 1L << (rnu - bucket); /* how many blocks to get */ siz = 1L << rnu;#ifndef NO_SBRK op = (union overhead *)sbrk(siz); /* no more room! */ if ((int)op == -1) return; /* * Round up to minimum allocation size boundary * and deduct from block count to reflect. */ if((int)op & (QUANTUM-1)) { op = (union overhead *)(((int)op + QUANTUM) &~ (QUANTUM-1)); nblks--; }#else op = (union overhead *)malloc(siz); /* no more room! */ if (!op) return;#endif /* * Add new memory allocated to the * free list for this hash bucket. */ nextf[bucket] = op; siz = 1L << bucket; while (--nblks) { op->ov_next = (union overhead *)((caddr_t)op + siz); op = op->ov_next; }}/* * NAME * mem_alloc - memory allocator * * SYNOPSIS * char * * mem_alloc() * * DESCRIPTION * Mem_alloc is used to allocate memory large enought to fit the requested * size, and on a boundary suitable for placing any value. * * RETURNS * char *, pointer to base of dynamic memory allocated * * CAVEAT * Use mem_free() when you are finished with the space. */char *mem_alloc(nbytes) register unsigned long int nbytes;{ register union overhead *p; register int bucket; register unsigned long int shiftr; if (nbytes > ((unsigned int) -1)) return NULL; assert((watchloc == NULL) || (watchloc->ov_magic == MAGIC), 12); /* * Convert amount of memory requested into * closest block size stored in hash buckets * which satisfies request. Account for * space used per block for accounting. */ nbytes = (nbytes + sizeof (union overhead) + RSLOP + (QUANTUM-1)) &~ (QUANTUM-1); shiftr = (nbytes - 1) >> QUANTUM_NBITS; /* apart from this loop, this is O(1) */ bucket = QUANTUM_NBITS; while(shiftr) { shiftr >>= 1; bucket++; } /* * If nothing in hash bucket right now, * request more memory from the system. */ if (!nextf[bucket]) morecore(bucket); if (!(p = nextf[bucket])) return (char*)0; /* remove from linked list */ nextf[bucket] = p->ov_next; p->ov_magic = MAGIC; p->ov_index = bucket;#ifdef MSTATS nmalloc[bucket]++;#endif#ifdef RCHECK /* * Record allocated size of block and * bound space with magic numbers */ if (nbytes <= (1L<<16)) p->ov_size = nbytes - 1; p->ov_rmagic = RMAGIC; *((u_int *)((caddr_t)p + nbytes - RSLOP)) = RMAGIC;#endif return ((char *)(p + 1));}/* * NAME * mem_free - free memory * * SYNOPSIS * int * mem_free(cp) * char * cp; * * DESCRIPTION * Mem_free is used to release space allocated by mem_alloc * or mem_realloc. * * RETURNS * int * * CAVEAT * do not pass mem_free() an argument that was returned by mem_alloc() * or mem_realloc(). */intmem_free(cp) char * cp;{ register u_int bucket; register union overhead *op; assert((watchloc == NULL) || (watchloc->ov_magic == MAGIC), 13); if (!cp) return; op = (union overhead *)cp - 1; assert(op->ov_magic == MAGIC, 5); /* make sure it was in use */ assert(op->ov_index < NBUCKETS, 6); assert(op->ov_index >= QUANTUM_NBITS, 7);#ifdef RCHECK assert(op->ov_index > 16 || op->ov_size == (1L<<op->ov_index)-1, 8); assert(op->ov_rmagic == RMAGIC, 9); assert(op->ov_index > 16 || *(u_int *)((caddr_t)op + op->ov_size + 1 - RSLOP) == RMAGIC, 10);#endif#ifndef DEBUG if(op->ov_magic != MAGIC) return; /* sanity */#endif bucket = op->ov_index; op->ov_next = nextf[bucket]; nextf[bucket] = op;#ifdef MSTATS nmalloc[bucket]--;#endif}/* * NAME * findbucket - find a bucket * * SYNOPSIS * int * findbucket(freep, srchlen) * union overhead * freep; * int srchlen; * * DESCRIPTION * Findbucket is used to find the bucket a free block is in. * Search ``srchlen'' elements of each free list for a block whose * header starts at ``freep''. If srchlen is -1 search the whole list. * * RETURNS * bucket number, or -1 if not found. */static intfindbucket(freep, srchlen) union overhead * freep; int srchlen;{ register union overhead *p; register int i, j; for (i = 0; i < NBUCKETS; i++) { j = 0; for (p = nextf[i]; p && j != srchlen; p = p->ov_next) { if (p == freep) return i; j++; } } return -1;}/* * When a program attempts "storage compaction" as mentioned in the * old malloc man page, it realloc's an already freed block. Usually * this is the last block it freed; occasionally it might be farther * back. We have to search all the free lists for the block in order * to determine its bucket: first we make one pass thru the lists * checking only the first block in each; if that fails we search * ``realloc_srchlen'' blocks in each list for a match (the variable * is extern so the caller can modify it). If that fails we just copy * however many bytes was given to realloc() and hope it's not huge. */static int realloc_srchlen = 4; /* 4 should be plenty, -1 =>'s whole list *//* * NAME * mem_realloc - change size * * SYNOPSIS * char * mem_realloc(cp, nbytes) * char * cp; * u_int nbytes; * * DESCRIPTION * Mem_realloc is used to enlarge a chunk of memory * returned by mem_alloc() or mem_realloc(). * * RETURNS * char *, pointer to base of dynamic memory allocated * * CAVEAT * Use mem_free() when you are finished with the space. */char *mem_realloc(cp, nbytes) char *cp; unsigned long nbytes;{ register u_int old_nbytes; register union overhead *op; char * res; register u_int old_bucket; short was_alloced = 0; if (nbytes > ((unsigned int) -1)) return NULL; assert((watchloc == NULL) || (watchloc->ov_magic == MAGIC), 14); if (!cp) return mem_alloc(nbytes); op = (union overhead *)((caddr_t)cp - sizeof (union overhead)); if (op->ov_magic == MAGIC) { was_alloced++; old_bucket = op->ov_index; } else { /* * Already free, doing "compaction". * * Search for the old block of memory on the * free list. First, check the most common * case (last element free'd), then (this failing) * the last ``realloc_srchlen'' items free'd. * If all lookups fail, then assume the size of * the memory block being realloc'd is the * smallest possible. */ if ( (old_bucket = findbucket(op, 1)) == -1 && (old_bucket = findbucket(op, realloc_srchlen)) == -1 ) old_bucket = QUANTUM_NBITS; } old_nbytes = (1L << old_bucket) - sizeof(union overhead) - RSLOP; /* * avoid the copy if same size block */ if ( was_alloced && nbytes <= old_nbytes && nbytes > (old_nbytes >> 1) - sizeof(union overhead) - RSLOP ) return cp; /* * grab another chunk */ if(!(res = mem_alloc(nbytes))) return (char*)0; assert(cp != res, 11); memcpy(res, cp, (nbytes < old_nbytes) ? nbytes : old_nbytes); if(was_alloced) mem_free(cp); return res;}#else /*CALC_MALLOC*/#undef MSTATS#endif /*CALC_MALLOC*//* * Allocate a new item from the specified free list. * Returns NULL if no item can be allocated. */ALLOCITEM *allocitem(fp) FREELIST *fp; /* free list header */{ FREEITEM *ip; /* allocated item */ if (fp->curfree > 0) { fp->curfree--; ip = fp->freelist; fp->freelist = ip->next; return (ALLOCITEM *) ip; } ip = (FREEITEM *) malloc(fp->itemsize); if (ip == NULL) return NULL; return (ALLOCITEM *) ip;}/* * Free an item by placing it back on a free list. * If too many items are on the list, it is really freed. */voidfreeitem(fp, ip) FREELIST *fp; /* freelist header */ FREEITEM *ip; /* item to be freed */{ if (ip == NULL) return; if (fp->curfree >= fp->maxfree) { free((char *) ip); return; } ip->next = fp->freelist; fp->freelist = ip; fp->curfree++;}/* * NAME * mem_stats - print memory statistics * * SYNOPSIS * void * mem_stats(s) * char * s; * * DESCRIPTION * Mem_stats is used to print out statistics about current memory usage. * ``s'' is the title string * * Prints two lines of numbers, one showing the length of the free list * for each size category, the second showing the number of mallocs - * frees for each size category. * * RETURNS * void *//*ARGSUSED*/voidmem_stats(s) char * s;{#ifdef MSTATS register int i, j; register union overhead *p; int totfree = 0; int totused = 0; fprintf(stderr, "Memory allocation statistics %s\n", s); fprintf(stderr, "%11s:%12s%12s%12s\n", "Bucket", "In Use", "Free", "Sum"); for (i = 0; i < NBUCKETS; i++) { for (j = 0, p = nextf[i]; p; p = p->ov_next, j++) ; if(!j && !nmalloc[i]) continue; fprintf(stderr, "%11d:%12d%12d%12d\n", (1L<<i), nmalloc[i], j, j+nmalloc[i]); totfree += j * (1L << i); totused += nmalloc[i] * (1L << i); } fprintf(stderr, "%11s:%12d%12d%12d\n", "Totals", totused, totfree, totused+totfree);#else fprintf(stderr, "Memory allocation stats were not compiled into calc\n");#endif}#ifdef DEBUGvoidassertfailed(n){ printf("Assertion %d failed\n", n); exit(1);}#endif/* END CODE */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -