Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

kmalloc, kfree and ksize

kmalloc

This is the function that allocates memory for the kernel. Its simpler than one might think. All it does is, is walk the list of blocks and finds one that is big enough to hold our data, then splits of a small piece to use and save the rest as free memory to be used.

We have already done block headers so kmalloc just has to edit and create new ones accordingly.

Kmalloc follows a simple loop condition that if the head is free and the available size is enough then continue. Otherwise it means we have run out of space and can’t allocate. In that case we send back None. If it was successful is sends back the pointer as a Option<*mut u8>

Where to begin

First of all we need to find the heap and begin walking. We already know where this is so we can use this call to save it to out head

let mut head = HEAP_START as *mut BlockHeader;

That’s a Bingo

If we find a block that meets our condition…

"if the head is free and the available size is enough then continue"

…then we need to edit the header is some way. We immediately set free to false and decided what to do next. The first option is if the block leaves space over too small for a new allocation. In this case we just give the current block and set the free to false.

(*head).free = false;

Otherwise we need to create a new header, subtracting the new allocation.

// Save current size for later
let old_size = (*head).size;

// This block is now being used
(*head).free = false;

// Save the requested size
(*head).size = size as u32;

// Create a new next to hold the rest of the heap
let next = (head as *mut u8).add(HEADER_SIZE + size) as *mut BlockHeader;

// New block is set to free
(*next).free = true;

// Size is the old size minus both the header size and the requested size
(*next).size = old_size - size as u32 - HEADER_SIZE as u32;

// Set news next to the heads next and the heads next the the new to create a chain
(*next).next = (*head).next;
(*head).next = next;

We can then it returns a pointer to the user data region which immediately starts after the header.

return Some((head as *mut u8).add(HEADER_SIZE));

If for some reason, kmalloc can’t find valid block, it sends back the None:

return None;


kfree

There isn’t really much to say about kfree. All this function does is take the pointer given, moves to its header and sets the free bool to true.

if ptr.is_null() {
    return;
}
let header = ptr.sub(HEADER_SIZE) as *mut BlockHeader;
(*header).free = true;

ksize

ksize follows the same procedure. We already have a pointer so we just move to the header and return the size stored in the block

if ptr.is_null() {
    return 0;
}
let header = ptr.sub(HEADER_SIZE) as *mut BlockHeader;
(*header).size

Memtest

This acts as our sanity check and as a way to test these functions in our kernel.

We are essentially

  • Use kmalloc to allocate a block of 42bytes
  • Write 0x2A to the space
  • Read from the block
  • Check size with ksize
  • Free with kfree
> memtest
=== Testing kmalloc & co. ===
kmalloc(42): OK ptr: 0x25700c
Write 0x2A: OK
Read back:  42 OK
ksize:      44 OK
kfree:      OK