meminit, memget, memput, memstats -- memory allocation/deallocation system
#include <isc/memcluster.h>
void *
memget(size_t size);
void
memput(void *mem, size_t size);
void
memstats(FILE *out);
These functions access a memory management system which allows callers to
not fragment memory to the extent which can ordinarily occur through many
random calls to malloc(3). Instead, memget() gets a large contiguous
chunk of blocks of the requested size and parcels out these blocks as
requested. The symmetric call is memput(), which callers use to return a
piece of memory obtained from memget(). Statistics about memory usage
are returned by memstats(), which prints a report on the stream out.
INTERNALS [Toc] [Back]
Internally, linked lists of free memory blocks are stored in an array.
The size of this array is determined by the value MEM_FREECOUNT, currently
set to 1100. In general, for any requested blocksize ``size'',
any free blocks will be stored on the linked list at that index. No free
lists are managed for blocks greater than or equal to MEM_FREECOUNT
bytes; instead, calls to malloc(3) or free(3) are made, directly.
Since the blocks are actually stored as linked lists, they must at least
be large enough to hold a pointer to the next block. This size, which is
SMALL_SIZE_LIMIT, is currently defined as
#define SMALL_SIZE_LIMIT sizeof(struct { void *next; })
Both memget() and memput() enforce this limit; for example, any call to
memget() requesting a block smaller than SMALL_SIZE_LIMIT bytes will
actually be considered to be of size SMALL_SIZE_LIMIT internally. (Such
a caller request will be logged for memstats() purposes using the callerrequested
size; see the discussion of memstats(), below, for more information.)
Additionally, the requested size will be adjusted so that when a large
malloc(3)-d chunk of memory is broken up into a linked list, the blocks
will all fall on the correct memory alignment boundaries. Thus, one can
conceptualize a call which mentions size as resulting in a new_size which
is used internally.
In order to more efficiently allocate memory, there is a ``target'' size
for calls to malloc(3). It is given by the pre-defined value MEM_TARGET,
which is currently 4096 bytes. For any requested block size, enough memory
is malloc(3)-d in order to fill up a block of about MEM_TARGET bytes.
[NOTE: For allocations larger than MEM_TARGET/2 bytes, there is a ``fudge
factor'' introduced which boosts the target size by 25% of MEM_TARGET.
This means that enough memory for two blocks will actually be allocated
for any size such that (MEM_TARGET/ 3) < size < (MEM_TARGET*5/8), provided
that the value of MEM_FREECOUNT is at least as large as the upper
limit shown above.]
FUNCTION DESCRIPTIONS [Toc] [Back]
The function memget() returns a pointer to a block of memory of at least
the requested size. After adjusting size to the value new_size as mentioned
above in the INTERNALS subsection, the internal array of free
lists is checked. If there is no block of the needed new_size, then
memget() will malloc(3) a chunk of memory which is as many times as
new_size will fit into the target size. This memory is then turned into
a linked list of new_size-sized blocks which are given out as requested;
the last such block is the first one returned by memget(). If the
requested size is zero or negative, then NULL is returned and errno is
set to EINVAL; if size is larger than or equal to the pre-defined maximum
size MEM_FREECOUNT, then only a single block of exactly size will be
malloc(3)-d and returned.
The memput() call is used to return memory once the caller is finished
with it. After adjusting size the the value new_size as mentioned in the
INTERNALS subsection, above, the block is placed at the head of the free
list of new_size-sized blocks. If the given size is zero or negative,
then errno is set to EINVAL, as for memget(). If size is larger than or
equal to the pre-defined maximum size MEM_FREECOUNT, then the block is
immediately free(3)-d.
NOTE: It is important that callers give memput() only blocks of memory
which were previously obtained from memget() if the block is actually
less than SMALL_SIZE_LIMIT bytes in size. Since all blocks will be added
to a free list, any block which is not at least SMALL_SIZE_LIMIT bytes
long will not be able to hold a pointer to the next block in the free
list.
The memstats() function will summarize the number of calls to memget()
and memput() for any block size from 1 byte up to (MEM_FREECOUNT - 1)
bytes, followed by a single line for any calls using a size greater than
or equal to MEM_FREECOUNT; a brief header with shell-style comment lines
prefaces the report and explains the information. The FILE pointer out
identifies the stream which is used for this report. Currently,
memstat() reports the number of calls to memget() and memput() using the
caller-supplied value size; the percentage of outstanding blocks of a
given size (i.e., the percentage by which calls to memget() exceed
memput()) are also reported on the line for blocks of the given size.
However, the percent of blocks used is computed using the number of
blocks allocated according to the internal parameter new_size; it is the
percentage of blocks used to those available at a given new_size, and is
computed using the total number of caller ``gets'' for any caller size-s
which map to the internally-computed new_size. Keep in mind that
new_size is generally not equal to size, which has these implications:
1. For size smaller than SMALL_SIZE_LIMIT, memstat() will show
statistics for caller requests under size, but "percent used"
information about such blocks will be reported under
SMALL_SIZE_LIMIT-sized blocks.
2. As a general case of point 1, internal statistics are reported
on the the line corresponding to new_size, so that, for a
given caller-supplied size, the associated internal information
will appear on that line or on the next line which shows
"percent used" information.
NOTE: If the caller returns blocks of a given size and requests others of
size-s which map to the same internal new_size, it is possible for
memstats() to report usage of greater than 100% for blocks of size
new_size. This should be viewed as A Good Thing.
The function memget() returns a non-NULL pointer to a block of memory of
the requested size. It returns NULL if either the size is invalid (less
than or equal to zero) or a malloc(3) of a new block of memory fails. In
the former case, errno is set to EINVAL; in the latter, it is set to
ENOMEM.
Neither memput() nor memstats() return a value.
errno is set as follows:
EINVAL set by both memget() and memput() if the size is zero or
negative
ENOMEM set by memget() if a call to malloc(3) fails
free(3), malloc(3).
Steven J. Richardson and Paul Vixie, Vixie Enterprises.
4th Berkeley Distribution Month day, year 4th Berkeley Distribution
[ Back ] |