CS3210 Design Operating Systems

Taesoo Kim





Memory Management and Heap Allocator

Taesoo Kim

Administrivia

Q&A

Is there any other file systems as easy as FAT series?

Since Box uses allocate in its implementation and is considered “safe” by Rust, would it be possible to introduce/create an infinite number of Boxes which will take up all of a system’s memory and cause a memory error?

Agenda

Simple High-level Interfaces

  // allocate a memory region (an object)
  void *malloc(size_t size);
  // free a memory region
  void free(void *ptr);

  // allocate a memory region for an array
  void *calloc(size_t nmemb, size_t size);
  // resize/reallocate a memory region
  void *realloc(void *ptr, size_t size);

  // new Type == malloc(sizeof(Type))
  // new Type[size] == malloc(sizeof(Type)*size)

CS101: Heap Allocators

Q0. ptr = malloc(size); *ptr?
Q1. ptr = malloc(0); ptr == NULL?
Q2. ptr = malloc(-1); ptr == NULL?
Q3. ptr = malloc(size); ptr == NULL but valid? /* vaddr = 0 */

Q4. free(ptr); ptr == NULL?
Q5. free(ptr); *ptr?
Q6. free(NULL)?

CS101: Common Goals of Heap Allocators

  1. Performance → worse case/common path of alloc/free
  2. Memory fragmentation → internal / external fragmentation
  3. Security (out-of-scope, but check cs6265!)

ptmalloc in Linux: Memory Allocation

ptr = malloc(size);

ptmalloc in Linux: Data Structure

struct malloc_chunk {
  [...]
  // double links for free chunks in small/large bins
  //  (only valid when this chunk is freed)
  struct malloc_chunk* fd;
  struct malloc_chunk* bk;
  
  // double links for next larger/smaller size in largebins
  //  (only valid when this chunk is freed)
  struct malloc_chunk* fd_nextsize;
  struct malloc_chunk* bk_nextsize;
};

ptmalloc in Linux: Memory Free

Common Design Choices

  1. Binning: size-base groups/operations
    • e.g., caching the same size objects together
  2. In-place metadata: metadata before/after or even inside
    • e.g., putting metadata inside the freed region
  3. Cardinal metadata: no encoding, direct pointers and sizes
    • e.g., using raw pointers for linked lists

Understanding Modern Heap Allocators

Wait, we are the OS kernel?

Wait, we are the OS kernel?

GlobalAllocator in Rust

// in std::alloc::GlobalAlloc
unsafe impl GlobalAlloc {
  unsafe fn alloc(&self, layout: Layout) -> *mut u8 {}
  unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {}
}

pub struct Layout {
    size_: usize,
    align_: NonZeroUsize,
}

Comparing with native heap allocator

unsafe fn alloc(&self, layout: Layout) -> *mut u8 {}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {}

vs.,

Q0. ptr = malloc(size); *ptr?
Q1. ptr = malloc(0); ptr == NULL?
Q2. ptr = malloc(-1); ptr == NULL?
Q3. ptr = malloc(size); ptr == NULL but valid? /* vaddr = 0 */

Q4. free(ptr); ptr == NULL?
Q5. free(ptr); *ptr?
Q6. free(NULL)?

Two naive implementations

Ref. Allocator Designs

Bump allocator

Maintaining free chunks for reuse

Bin allocator

      Bins
sz=16 [ -]--->[fd]--->[fd]-->NULL
sz=24 [ -]--->[fd]--->NULL
sz=32 [ -]--->NULL
       ...

Problems of bin allocator

References