home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Media Share 9
/
MEDIASHARE_09.ISO
/
hamradio
/
s920603.zip
/
ALLOC.C
next >
Wrap
C/C++ Source or Header
|
1992-05-21
|
12KB
|
500 lines
/* memory allocation routines
* Copyright 1991 Phil Karn, KA9Q
*
* Adapted from alloc routine in K&R; memory statistics and interrupt
* protection added for use with net package. Must be used in place of
* standard Turbo-C library routines because the latter check for stack/heap
* collisions. This causes erroneous failures because process stacks are
* allocated off the heap.
*/
#include <stdio.h>
#include <dos.h>
#include <alloc.h>
#include "global.h"
#include "mbuf.h"
#include "proc.h"
#include "cmdparse.h"
static unsigned long Memfail; /* Count of allocation failures */
static unsigned long Allocs; /* Total allocations */
static unsigned long Frees; /* Total frees */
static unsigned long Invalid; /* Total calls to free with garbage arg */
static unsigned long Intalloc; /* Calls to malloc with ints disabled */
static unsigned long Intfree; /* Calls to free with ints disabled */
static int Memwait; /* Number of tasks waiting for memory */
static unsigned long Yellows; /* Yellow alert garbage collections */
static unsigned long Reds; /* Red alert garbage collections */
unsigned long Availmem; /* Heap memory, ABLKSIZE units */
static unsigned long Morecores;
static unsigned long Sizes[16];
static int dostat __ARGS((int argc,char *argv[],void *p));
static int dofreelist __ARGS((int argc,char *argv[],void *p));
static int doibufsize __ARGS((int argc,char *argv[],void *p));
static int donibufs __ARGS((int argc,char *argv[],void *p));
static int dothresh __ARGS((int argc,char *argv[],void *p));
static int dosizes __ARGS((int argc,char *argv[],void *p));
struct cmds Memcmds[] = {
"freelist", dofreelist, 0, 0, NULLCHAR,
"ibufsize", doibufsize, 0, 0, NULLCHAR,
"nibufs", donibufs, 0, 0, NULLCHAR,
"sizes", dosizes, 0, 0, NULLCHAR,
"status", dostat, 0, 0, NULLCHAR,
"thresh", dothresh, 0, 0, NULLCHAR,
NULLCHAR,
};
#ifdef LARGEDATA
#define HUGE huge
#else
#define HUGE
#endif
union header {
struct {
union header HUGE *ptr;
unsigned long size;
} s;
long l[2];
};
typedef union header HEADER;
#define NULLHDR (HEADER HUGE *)NULL
#define ABLKSIZE (sizeof (HEADER))
static HEADER HUGE *morecore __ARGS((unsigned nu));
static HEADER Base;
static HEADER HUGE *Allocp = NULLHDR;
static unsigned long Heapsize;
/* Memory blocks obtained from MS-DOS by allocmem() call */
struct sysblock {
unsigned seg;
unsigned npar;
};
#define NSYSBLOCK 5
struct sysblock Sysblock[NSYSBLOCK];
/* Allocate block of 'nb' bytes */
void *
malloc(nb)
unsigned nb;
{
register HEADER HUGE *p, HUGE *q;
register unsigned nu;
int i;
if(!istate())
Intalloc++;
if(nb == 0)
return NULL;
/* Record the size of this request */
if((i = log2(nb)) >= 0)
Sizes[i]++;
/* Round up to full block, then add one for header */
nu = ((nb + ABLKSIZE - 1) / ABLKSIZE) + 1;
if((q = Allocp) == NULLHDR){
Base.s.ptr = Allocp = q = &Base;
Base.s.size = 1;
}
for(p = q->s.ptr; ; q = p, p = p->s.ptr){
if(p->s.size >= nu){
/* This chunk is at least as large as we need */
if(p->s.size <= nu + 1){
/* This is either a perfect fit (size == nu)
* or the free chunk is just one unit larger.
* In either case, alloc the whole thing,
* because there's no point in keeping a free
* block only large enough to hold the header.
*/
q->s.ptr = p->s.ptr;
} else {
/* Carve out piece from end of entry */
p->s.size -= nu;
p += p->s.size;
p->s.size = nu;
}
#ifdef circular
Allocp = q;
#endif
p->s.ptr = p; /* for auditing */
Allocs++;
Availmem -= p->s.size;
p++;
break;
}
if(p == Allocp && ((p = morecore(nu)) == NULLHDR)){
Memfail++;
break;
}
}
#ifdef LARGEDATA
/* On the brain-damaged Intel CPUs in "large data" model,
* make sure the pointer's offset field isn't null
* (unless the entire pointer is null).
* The Turbo C compiler and certain
* library functions like strrchr() assume this.
*/
if(FP_OFF(p) == 0 && FP_SEG(p) != 0){
/* Return denormalized but equivalent pointer */
return (void *)MK_FP(FP_SEG(p)-1,16);
}
#endif
return (void *)p;
}
/* Get more memory from the system and put it on the heap */
static HEADER HUGE *
morecore(nu)
unsigned nu;
{
char HUGE *cp;
HEADER HUGE *up;
unsigned size;
unsigned segp;
unsigned npar;
struct sysblock *sp;
int i;
Morecores++;
size = nu * ABLKSIZE;
/* First try to expand our main memory block */
if ((int)(cp = (char HUGE *)sbrk(size)) != -1){
up = (HEADER *)cp;
up->s.size = nu;
up->s.ptr = up; /* satisfy audit */
free((void *)(up + 1));
Heapsize += size;
Frees--; /* Nullify increment inside free() */
return Allocp;
}
/* That failed; the main memory block must have grown into another
* allocated block, or something else (e.g., the increase handles
* call in ioinit()) must have allocated memory just beyond it.
* Allocate or extend an additional memory block.
*/
npar = (size+16)/16; /* Convert size from bytes to paragraphs */
cp = NULL;
for(sp=Sysblock,i=0;i < NSYSBLOCK;i++,sp++){
if(sp->npar != 0){
/* Try to expand this block */
if(setblock(sp->seg,sp->npar + npar) != -1){
/* Failed (-1 == SUCCESS; strange!) */
continue;
}
/* Block expansion succeeded */
cp = MK_FP(sp->seg + sp->npar,0);
sp->npar += npar;
} else {
/* Allocate new block */
if(allocmem(npar,&segp) != -1){
return NULL; /* Complete failure */
}
/* -1 indicates SUCCESS (strange) */
sp->seg = segp;
sp->npar = npar;
cp = MK_FP(segp,0);
}
break;
}
if(cp != (char HUGE *)NULL){
/* Expand or create succeeded, add to heap */
up = (HEADER *)cp;
up->s.size = (npar*16)/ABLKSIZE;
up->s.ptr = up; /* satisfy audit */
free((void *)(up + 1));
Heapsize += npar*16;
Frees--; /* Nullify increment inside free() */
return Allocp;
}
return NULL;
}
/* Put memory block back on heap */
void
free(blk)
void *blk;
{
register HEADER HUGE *p, HUGE *q;
unsigned short HUGE *ptr;
if(!istate())
Intfree++;
if(blk == NULL)
return; /* Required by ANSI */
p = (HEADER HUGE *)blk - 1;
/* Audit check */
if(p->s.ptr != p){
ptr = (unsigned short *)&blk;
printf("free: WARNING! invalid pointer (0x%lx) proc %s\n",
ptol(blk),Curproc->name);
stktrace();
Invalid++;
log(-1,"free: WARNING! invalid pointer (0x%lx) pc = 0x%x %x proc %s\n",
ptol(blk),ptr[-1],ptr[-2],Curproc->name);
return;
}
Availmem += p->s.size;
/* Search the free list looking for the right place to insert */
for(q = Allocp; !(p > q && p < q->s.ptr); q = q->s.ptr){
/* Highest address on circular list? */
if(q >= q->s.ptr && (p > q || p < q->s.ptr))
break;
}
if(p + p->s.size == q->s.ptr){
/* Combine with front of this entry */
p->s.size += q->s.ptr->s.size;
p->s.ptr = q->s.ptr->s.ptr;
} else {
/* Link to front of this entry */
p->s.ptr = q->s.ptr;
}
if(q + q->s.size == p){
/* Combine with end of this entry */
q->s.size += p->s.size;
q->s.ptr = p->s.ptr;
} else {
/* Link to end of this entry */
q->s.ptr = p;
}
#ifdef circular
Allocp = q;
#endif
Frees++;
if(Memwait != 0)
psignal(&Memwait,0);
}
#ifdef notdef /* Not presently used */
/* Move existing block to new area */
void *
realloc(area,size)
void *area;
unsigned size;
{
unsigned osize;
HEADER HUGE *hp;
char HUGE *cp;
hp = ((HEADER *)area) - 1;
osize = (hp->s.size -1) * ABLKSIZE;
free(area);
if((cp = malloc(size)) != NULL && cp != area)
memcpy((char *)cp,(char *)area,size>osize? osize : size);
return cp;
}
#endif
/* Allocate block of cleared memory */
void *
calloc(nelem,size)
unsigned nelem; /* Number of elements */
unsigned size; /* Size of each element */
{
register unsigned i;
register char *cp;
i = nelem * size;
if((cp = malloc(i)) != NULL)
memset(cp,0,i);
return cp;
}
/* Version of malloc() that waits if necessary for memory to become available */
void *
mallocw(nb)
unsigned nb;
{
register void *p;
while((p = malloc(nb)) == NULL){
Memwait++;
pwait(&Memwait);
Memwait--;
}
return p;
}
/* Version of calloc that waits if necessary for memory to become available */
void *
callocw(nelem,size)
unsigned nelem; /* Number of elements */
unsigned size; /* Size of each element */
{
register unsigned i;
register char *cp;
i = nelem * size;
cp = mallocw(i);
memset(cp,0,i);
return cp;
}
/* Return 0 if at least Memthresh memory is available. Return 1 if
* less than Memthresh but more than Memthresh/2 is available; i.e.,
* if a yellow garbage collection should be performed. Return 2 if
* less than Memthresh/2 is available, i.e., a red collection should
* be performed.
*/
int
availmem()
{
void *p;
if(Availmem*ABLKSIZE >= Memthresh)
return 0; /* We're clearly OK */
/* There's not enough on the heap; try calling malloc to see if
* it can get more from the system
*/
if((p = malloc(Memthresh)) != NULL){
free(p);
return 0; /* Okay */
}
if((p = malloc(Memthresh/2)) != NULL){
free(p);
return 1; /* Yellow alert */
}
return 2; /* Red alert */
}
/* Print heap stats */
static int
dostat(argc,argv,envp)
int argc;
char *argv[];
void *envp;
{
struct sysblock *sp;
int i;
printf("heap size %lu avail %lu (%lu%%) morecores %lu\n",
Heapsize,Availmem * ABLKSIZE,100L*Availmem*ABLKSIZE/Heapsize,
Morecores);
if(Sysblock[0].npar != 0){
printf("Extra blocks:");
for(i=0,sp=Sysblock;i< NSYSBLOCK;i++,sp++){
if(sp->npar == 0)
break;
printf(" (%x0-%x0)",sp->seg,sp->seg+sp->npar);
}
printf("\n");
}
printf("allocs %lu frees %lu (diff %lu) alloc fails %lu invalid frees %lu\n",
Allocs,Frees,Allocs-Frees,Memfail,Invalid);
printf("pushdown calls %lu pushdown calls to malloc %lu\n",
Pushdowns,Pushalloc);
printf("interrupts-off calls to malloc %lu free %lu\n",Intalloc,Intfree);
printf("garbage collections yellow %lu red %lu\n",Yellows,Reds);
iqstat();
return 0;
}
/* Print heap free list */
static int
dofreelist(argc,argv,envp)
int argc;
char *argv[];
void *envp;
{
HEADER HUGE *p;
int i = 0;
for(p = Base.s.ptr;p != (HEADER HUGE *)&Base;p = p->s.ptr){
printf("%5lx %6lu",ptol((void *)p),p->s.size * ABLKSIZE);
if(++i == 4){
i = 0;
if(printf("\n") == EOF)
return 0;
} else
printf(" | ");
}
if(i != 0)
printf("\n");
return 0;
}
static int
dosizes(argc,argv,p)
int argc;
char *argv[];
void *p;
{
int i;
for(i=0;i<16;i += 4){
printf("N>=%5u:%7ld| N>=%5u:%7ld| N>=%5u:%7ld| N>=%5u:%7ld\n",
1<<i,Sizes[i], 2<<i,Sizes[i+1],
4<<i,Sizes[i+2],8<<i,Sizes[i+3]);
}
return 0;
}
int
domem(argc,argv,p)
int argc;
char *argv[];
void *p;
{
return subcmd(Memcmds,argc,argv,p);
}
static int
donibufs(argc,argv,p)
int argc;
char *argv[];
void *p;
{
return setint(&Nibufs,"Interrupt pool buffers",argc,argv);
}
static int
doibufsize(argc,argv,p)
int argc;
char *argv[];
void *p;
{
return setuns(&Ibufsize,"Interrupt buffer size",argc,argv);
}
static int
dothresh(argc,argv,p)
int argc;
char *argv[];
void *p;
{
return setlong(&Memthresh,"Free memory threshold (bytes)",argc,argv);
}
/* Background memory compactor, used when memory runs low */
void
gcollect(i,v1,v2)
int i; /* Args not used */
void *v1;
void *v2;
{
void (**fp)();
int red;
for(;;){
pause(1000L); /* Run every second */
/* If memory is low, collect some garbage. If memory is VERY
* low, invoke the garbage collection routines in "red" mode.
*/
switch(availmem()){
case 0:
continue; /* All is well */
case 1:
red = 0;
Yellows++;
break;
case 2:
red = 1;
Reds++;
break;
}
for(fp = Gcollect;*fp != NULL;fp++)
(**fp)(red);
}
}