Memory Management
Xv6 handles memory in 4KB pages, and uses a free list to track available memory.
Memory allocation is done in the kalloc.c
file. Memory is held in a
free list, which has 4KB pages to dole out for memory allocation.
struct run {
struct run *next;
};
struct {
struct spinlock lock;
struct run *freelist;
} kmem;
There’s a variable, called end
, which denotes the first address after
the kernel starts:
extern char end[];
There’s a function to initialize the allocator by grabbing a lock for the entire freelist and freeing all of the pages it can access in memory.
void kinit() {
initlock(&kmem.lock, "kmem");
freerange(end, (void*)PHYSTOP);
}
freerange
frees all the pages in a provided range.
void freerange(void *pa_start, void *pa_end) {
char *p;
p = (char*)PGROUNDUP((uint64)pa_start);
for(; p + PGSIZE <= (char*)pa_end; p += PGSIZE)
kfree(p);
}
kalloc
allocates a page.
- It locks the free list
- It then traverses the list, finding the first available page
- It unlocks the free list
- It fills the page with 0x05 (junk)
- and returns the page
void * kalloc(void) {
struct run *r;
acquire(&kmem.lock);
r = kmem.freelist;
if(r)
kmem.freelist = r->next;
release(&kmem.lock);
if(r)
memset((char*)r, 5, PGSIZE); // fill with junk
return (void*)r;
}
kfree
frees a page of memory.
- It makes sure the address is page aligned
- It makes sure the address is in the range of start - end
- Fills the page with 0x01 to detect use-after-free bugs.
- Locks the memory allocator
- Adds the page back to the free list by adding it to the front of the list
- releases the free list’s lock.
void kfree(void *pa) {
struct run *r;
if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP)
panic("kfree");
// Fill with 1s to catch use-after-free
memset(pa, 1, PGSIZE);
r = (struct run*)pa;
acquire(&kmem.lock);
r->next = kmem.freelist;
kmem.freelist = r;
release(&kmem.lock);
}