May 10th Lecture ================== Kernel Memory Allocator ("man 9 malloc" for details) * used by kernel code that needs to allocate memory; both top half (syscalls) and bottom half (interrupt handlers) A few uses for kernel memory allocator: * pathname translation buffers (1k each) * I/O buffers * networking buffers * zombie structures * table entries (e.g., proc table) malloc(size, type, flags) -- allocates memory in the kernel address space for an object of size 'size;' memory is uninitialized unless flags specify otherwise free(addr, type) -- deallocates memory allocated by malloc(); memory is not zeroed; NULL addresses are simply ignored realloc(addr, size, type, flags) -- changes allocation size of addr; mem contents are unchanged up through lesser of new/old sizes * address returned may not match address passed in * if function fails; memory is untouched (old allocation remains) reallocf(addr, size, type, flags) -- exactly the same as realloc except that if reallocation fails, the old memory block will be freed Flags: * M_ZERO -- zero allocated memory * M_NOWAIT/M_WAITOK -- put current proc to sleep until we can allocate? okay for system calls, not okay for interrupt handlers * M_USE_RESERVE (deprecated) -- okay to dig into kernel's backup reserve to allocate enough memory type parameter -- classifies memory usage for debugging and sanity checks * use "vmstat -m" to see how kernel memory is currently allocated [== Kernel Memory Allocator (KMA) ==] * designed similar to user malloc routines * must make good use of physical memory * utilization = requested / required (required > requested due to fragmentation; 50% utilization is considered "good") * most important trait: speed - memory allocation is done frequently; a slow KMA degrades performance - if KMA is slow, different kernel subsystems and device drivers may feel the need to build their own caching MA's on top of it...wasteful (free memory is split between different subsystems and not pooled in one place...can get OOM even when plenty of memory is actually available) Design Considerations for a KMA vs User Memory Allocator (UMA) * max allocation for KMA is determined at system boot (i.e., < total physical memory in system); can use small, static structures for bookkeeping * UMA can allocate any amount of memory up to the size of the VM space; not bounded by physical memory - using static structures for this would have huge space requirements - using dynamic structures is more complex; there are more potential sources of failure * kernel can manually page out parts of its address space if they aren't needed in the near future; more direct control over working set than user processes have BSD 4.3 was when general purpose KMA was first added; multiple UMA's analyzed for base design fastest at that time was the 4.2 BSD UMA (very fast, but also very wasteful in terms of utilization) BSD 4.2 UMA: * set of lists of increasing powers of 2 for cached allocation blocks * size of a allocation request is rounded up to next highest power of 2; if cache list for that size is non-empty, return a block from that list; if cache list is empty, allocate more memory from the heap * deallocation very fast...simply return block to appropriate list BSD 4.3 KMA: * hybrid of BSD 4.2 UMA "power of 2" system and slower, but more memory efficient "first fit" allocator * small allocations use power of 2 list strategy * allocations >= 2k use different strategy (95-98% of allocs are <= 1k) - allocation size rounded up to multiple of page size (1k on most systems at that time) - scan memory for free pages to use for allocation - e.g., a 5K request takes exactly 5k rather than 8k Further optimizations in later versions: * use "zone allocator" (to be discussed later) to allocate pages for blocks of each power of 2 size; carve pages up into appropriate block sizes * when all blocks in a page are full, allocate another entire page and carve it up * first fit algorithm now used for allocations > page size (modern page sizes are usually ~4k)