implement tree allocator
This commit is contained in:
149
c_os/kernel/allocator/TreeAllocator.cc
Executable file
149
c_os/kernel/allocator/TreeAllocator.cc
Executable file
@ -0,0 +1,149 @@
|
||||
#include "kernel/allocator/TreeAllocator.h"
|
||||
#include "kernel/Globals.h"
|
||||
#include <stddef.h>
|
||||
|
||||
// NOTE: I added this file
|
||||
|
||||
void TreeAllocator::init() {
|
||||
this->free_start = (tree_block_t*)this->heap_start;
|
||||
this->free_start->allocated = false;
|
||||
this->free_start->left = NULL;
|
||||
this->free_start->right = NULL;
|
||||
this->free_start->parent = NULL;
|
||||
this->free_start->next = (list_block_t*)this->free_start;
|
||||
this->free_start->previous = (list_block_t*)this->free_start;
|
||||
|
||||
kout << "Initialized Tree Allocator" << endl
|
||||
<< " - Heap Start: " << hex << (unsigned int)this->heap_start
|
||||
<< ", Heap End: " << hex << (unsigned int)this->heap_end
|
||||
<< ", Heap Size: " << hex << this->heap_size << endl;
|
||||
kout << endl;
|
||||
}
|
||||
|
||||
void TreeAllocator::dump_free_memory() {
|
||||
kout << "Free Memory:" << endl;
|
||||
this->dump_free_memory(this->free_start);
|
||||
}
|
||||
|
||||
void TreeAllocator::dump_free_memory(tree_block_t* node) {
|
||||
if (node == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->dump_free_memory(node->left);
|
||||
kout << " - Free Block at " << hex << (unsigned int)node << " (" << hex << this->get_size(node) << " Byte)" << endl;
|
||||
this->dump_free_memory(node->right);
|
||||
}
|
||||
|
||||
void* TreeAllocator::alloc(unsigned int req_size) {
|
||||
kout << "Requested " << dec << req_size << " Bytes" << endl;
|
||||
|
||||
// Round to word borders + tree_block size
|
||||
unsigned int rreq_size = req_size;
|
||||
if (rreq_size < sizeof(tree_block_t) - sizeof(list_block_t)) {
|
||||
// the list_block_t is part of every block, but when freeing
|
||||
// memory we need enough space to store the rbt metadata
|
||||
rreq_size = sizeof(tree_block_t) - sizeof(list_block_t);
|
||||
kout << " - Increased block size for rbt metadata" << endl;
|
||||
}
|
||||
unsigned int req_size_diff = (BASIC_ALIGN - rreq_size % BASIC_ALIGN) % BASIC_ALIGN;
|
||||
rreq_size = rreq_size + req_size_diff;
|
||||
if (req_size_diff > 0) {
|
||||
kout << " - Rounded to word border (+" << dec << req_size_diff << " bytes)" << endl;
|
||||
}
|
||||
|
||||
// Finds smallest block that is large enough
|
||||
tree_block_t* best_fit = this->rbt_search_bestfit(rreq_size);
|
||||
if (best_fit == NULL) {
|
||||
kout << " - No block found" << endl;
|
||||
return NULL;
|
||||
}
|
||||
if (best_fit->allocated) {
|
||||
// Something went really wrong
|
||||
kout << " - Block already allocated :(" << endl;
|
||||
return NULL;
|
||||
}
|
||||
best_fit->allocated = true;
|
||||
unsigned int size = this->get_size(best_fit);
|
||||
kout << " - Found best-fit: " << hex << (unsigned int)best_fit << endl;
|
||||
|
||||
// Remove the block first so we can insert correctly when cutting
|
||||
kout << " - Removing block from freelist" << endl;
|
||||
this->rbt_remove(best_fit);
|
||||
if (size > HEAP_MIN_FREE_BLOCK_SIZE + rreq_size + sizeof(list_block_t)) {
|
||||
// Block can be cut
|
||||
kout << " - Allocating " << dec << rreq_size << " Bytes with cutting" << endl;
|
||||
|
||||
// [best_fit_start | sizeof(list_block_t) | rreq_size | new_block_start]
|
||||
tree_block_t* new_block = (tree_block_t*)((char*)best_fit + sizeof(list_block_t) + rreq_size);
|
||||
new_block->allocated = false;
|
||||
this->dll_insert(best_fit, new_block);
|
||||
this->rbt_insert(new_block);
|
||||
} else {
|
||||
// Don't cut block
|
||||
// The block is already correctly positioned in the linked list so we only
|
||||
// need to remove it from the freelist, which is done for both cases
|
||||
kout << " - Allocating " << dec << rreq_size << " Bytes without cutting" << endl;
|
||||
}
|
||||
|
||||
kout << " - Returned address " << hex << (unsigned int)((char*)best_fit + sizeof(list_block_t)) << endl;
|
||||
return (void*)((char*)best_fit + sizeof(list_block_t));
|
||||
}
|
||||
|
||||
void TreeAllocator::free(void* ptr) {
|
||||
kout << "Freeing " << hex << (unsigned int)ptr << endl;
|
||||
|
||||
list_block_t* block = (list_block_t*)((char*)ptr - sizeof(list_block_t));
|
||||
if (!block->allocated) {
|
||||
// Block already free
|
||||
return;
|
||||
}
|
||||
block->allocated = false;
|
||||
|
||||
list_block_t* previous = block->previous;
|
||||
list_block_t* next = block->next;
|
||||
|
||||
if (next->allocated && previous->allocated) {
|
||||
// No merge
|
||||
this->rbt_insert((tree_block_t*)block);
|
||||
}
|
||||
|
||||
if (!next->allocated) {
|
||||
// Merge forward
|
||||
kout << " - Merging forward" << endl;
|
||||
|
||||
// Remove the next block from all lists as it is now part of our freed block
|
||||
this->dll_remove(next);
|
||||
this->rbt_remove((tree_block_t*)next);
|
||||
if (previous->allocated) {
|
||||
// Don't insert if removed later
|
||||
this->rbt_insert((tree_block_t*)block);
|
||||
}
|
||||
}
|
||||
|
||||
if (!previous->allocated) {
|
||||
// Merge backward
|
||||
kout << " - Merging backward" << endl;
|
||||
|
||||
// Remove the current block from all lists as it is now part of the previous block
|
||||
// It doesn't have to be removed from rbt as it wasn't in there as it was allocated before
|
||||
this->dll_remove(block);
|
||||
this->rbt_remove((tree_block_t*)previous);
|
||||
this->rbt_insert((tree_block_t*)previous);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int TreeAllocator::get_size(list_block_t* block) const {
|
||||
if (block->next == block) {
|
||||
// Only one block exists
|
||||
return this->heap_end - ((unsigned int)block + sizeof(list_block_t));
|
||||
}
|
||||
|
||||
if ((unsigned int)block->next > (unsigned int)block) {
|
||||
// Next block is placed later in memory
|
||||
return (unsigned int)block->next - ((unsigned int)block + sizeof(list_block_t));
|
||||
}
|
||||
|
||||
// Next block is placed earlier in memory which means block is at memory end
|
||||
return (unsigned int)this->heap_end - ((unsigned int)block + sizeof(list_block_t));
|
||||
}
|
||||
80
c_os/kernel/allocator/TreeAllocator.h
Executable file
80
c_os/kernel/allocator/TreeAllocator.h
Executable file
@ -0,0 +1,80 @@
|
||||
#ifndef __TreeAllocator_include__
|
||||
#define __TreeAllocator_include__
|
||||
|
||||
#include "kernel/Allocator.h"
|
||||
|
||||
#define HEAP_MIN_FREE_BLOCK_SIZE 64 // min. Groesse eines freien Blocks
|
||||
#define BASIC_ALIGN 8
|
||||
|
||||
// NOTE: I added this file
|
||||
|
||||
typedef struct list_block {
|
||||
// Doubly linked list for every block
|
||||
bool allocated;
|
||||
struct list_block* next;
|
||||
struct list_block* previous;
|
||||
} list_block_t;
|
||||
|
||||
// Format eines freien Blocks
|
||||
// The free blocks are organized in a red-black tree to enable fast insertion with best-fit strategy.
|
||||
// To allow fast merging of freed blocks every block is part of a doubly linked list.
|
||||
// Because the red-black tree only contains the free blocks, the memory overhead comes
|
||||
// down to only 4 + 4 + 4 Bytes for the allocated flag, next and previous pointers.
|
||||
// The size can be calculated by using the next pointer so it doesn't have to be stored.
|
||||
typedef struct tree_block {
|
||||
// Doubly linked list for every block
|
||||
// Locate this at the beginning so we can just cast to allocated_block_t and overwrite the rbt data
|
||||
bool allocated;
|
||||
struct list_block* next;
|
||||
struct list_block* previous;
|
||||
// RB tree for free blocks
|
||||
struct tree_block* left;
|
||||
struct tree_block* right;
|
||||
struct tree_block* parent;
|
||||
bool red; // RB tree node color
|
||||
} tree_block_t;
|
||||
|
||||
class TreeAllocator : Allocator {
|
||||
|
||||
private:
|
||||
// Root of the rbt
|
||||
tree_block_t* free_start;
|
||||
|
||||
TreeAllocator(Allocator& copy); // Verhindere Kopieren
|
||||
|
||||
// Returns the size of the usable memory of a block
|
||||
unsigned int get_size(list_block_t* block) const;
|
||||
unsigned int get_size(tree_block_t* block) const { return this->get_size((list_block_t*)block); }
|
||||
|
||||
void dump_free_memory(tree_block_t* node);
|
||||
|
||||
// NOTE: Would be nice to have this stuff somewhere else for general use,
|
||||
// but that would require different rbt_node/dll_node structures.
|
||||
// If I need this again later I should look into it.
|
||||
void rbt_rot_l(tree_block_t* x);
|
||||
void rbt_rot_r(tree_block_t* x);
|
||||
void rbt_transplant(tree_block_t* a, tree_block_t* b);
|
||||
tree_block_t* rbt_minimum(tree_block_t* node);
|
||||
|
||||
void rbt_insert(tree_block_t* node);
|
||||
void rbt_fix_insert(tree_block_t* k);
|
||||
void rbt_remove(tree_block_t* z);
|
||||
void rbt_fix_remove(tree_block_t* x);
|
||||
|
||||
tree_block_t* rbt_search_bestfit(tree_block_t* node, unsigned int req_size);
|
||||
tree_block_t* rbt_search_bestfit(unsigned int req_size) { return this->rbt_search_bestfit(this->free_start, req_size); }
|
||||
|
||||
void dll_insert(list_block_t* previous, list_block_t* node);
|
||||
void dll_insert(tree_block_t* previous, tree_block_t* node) { this->dll_insert((list_block_t*)previous, (list_block_t*)node); }
|
||||
void dll_remove(list_block_t* node);
|
||||
|
||||
public:
|
||||
TreeAllocator() {};
|
||||
|
||||
void init();
|
||||
void dump_free_memory();
|
||||
void* alloc(unsigned int req_size);
|
||||
void free(void* ptr);
|
||||
};
|
||||
|
||||
#endif
|
||||
302
c_os/kernel/allocator/TreeAllocatorOperations.cc
Executable file
302
c_os/kernel/allocator/TreeAllocatorOperations.cc
Executable file
@ -0,0 +1,302 @@
|
||||
#include "kernel/allocator/TreeAllocator.h"
|
||||
#include "kernel/Globals.h"
|
||||
#include <stddef.h>
|
||||
|
||||
// NOTE: I added this file
|
||||
// NOTE: RBT code taken from https://github.com/Bibeknam/algorithmtutorprograms
|
||||
|
||||
// START copy from algorithmtutorprograms
|
||||
void TreeAllocator::rbt_transplant(tree_block_t* a, tree_block_t* b) {
|
||||
if (a->parent == NULL) {
|
||||
this->free_start = b;
|
||||
} else if (a == a->parent->left) {
|
||||
a->parent->left = b;
|
||||
} else {
|
||||
a->parent->right = b;
|
||||
}
|
||||
b->parent = a->parent;
|
||||
}
|
||||
|
||||
// insert the key to the tree in its appropriate position
|
||||
// and fix the tree
|
||||
void TreeAllocator::rbt_insert(tree_block_t* node) {
|
||||
// Ordinary Binary Search Insertion
|
||||
node->parent = NULL;
|
||||
node->left = NULL;
|
||||
node->right = NULL;
|
||||
node->red = true; // new node must be red
|
||||
|
||||
tree_block_t* y = NULL;
|
||||
tree_block_t* x = this->free_start;
|
||||
|
||||
while (x != NULL) {
|
||||
y = x;
|
||||
if (this->get_size(node) < this->get_size(x)) {
|
||||
x = x->left;
|
||||
} else {
|
||||
x = x->right;
|
||||
}
|
||||
}
|
||||
|
||||
// y is parent of x
|
||||
node->parent = y;
|
||||
if (y == NULL) {
|
||||
this->free_start = node;
|
||||
} else if (this->get_size(node) < this->get_size(y)) {
|
||||
y->left = node;
|
||||
} else {
|
||||
y->right = node;
|
||||
}
|
||||
|
||||
// if new node is a root node, simply return
|
||||
if (node->parent == NULL) {
|
||||
node->red = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// if the grandparent is null, simply return
|
||||
if (node->parent->parent == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Fix the tree
|
||||
this->rbt_fix_insert(node);
|
||||
}
|
||||
|
||||
// fix the red-black tree
|
||||
void TreeAllocator::rbt_fix_insert(tree_block_t* k) {
|
||||
tree_block_t* u;
|
||||
while (k->parent->red) {
|
||||
if (k->parent == k->parent->parent->right) {
|
||||
u = k->parent->parent->left; // uncle
|
||||
if (u->red) {
|
||||
// case 3.1
|
||||
u->red = false;
|
||||
k->parent->red = false;
|
||||
k->parent->parent->red = true;
|
||||
k = k->parent->parent;
|
||||
} else {
|
||||
if (k == k->parent->left) {
|
||||
// case 3.2.2
|
||||
k = k->parent;
|
||||
this->rbt_rot_r(k);
|
||||
}
|
||||
// case 3.2.1
|
||||
k->parent->red = false;
|
||||
k->parent->parent->red = true;
|
||||
this->rbt_rot_l(k->parent->parent);
|
||||
}
|
||||
} else {
|
||||
u = k->parent->parent->right; // uncle
|
||||
|
||||
if (u->red) {
|
||||
// mirror case 3.1
|
||||
u->red = false;
|
||||
k->parent->red = false;
|
||||
k->parent->parent->red = true;
|
||||
k = k->parent->parent;
|
||||
} else {
|
||||
if (k == k->parent->right) {
|
||||
// mirror case 3.2.2
|
||||
k = k->parent;
|
||||
this->rbt_rot_l(k);
|
||||
}
|
||||
// mirror case 3.2.1
|
||||
k->parent->red = false;
|
||||
k->parent->parent->red = true;
|
||||
this->rbt_rot_r(k->parent->parent);
|
||||
}
|
||||
}
|
||||
if (k == this->free_start) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
this->free_start->red = false;
|
||||
}
|
||||
|
||||
// rotate left at node x
|
||||
void TreeAllocator::rbt_rot_l(tree_block_t* x) {
|
||||
tree_block_t* y = x->right;
|
||||
x->right = y->left;
|
||||
if (y->left != NULL) {
|
||||
y->left->parent = x;
|
||||
}
|
||||
y->parent = x->parent;
|
||||
if (x->parent == nullptr) {
|
||||
this->free_start = y;
|
||||
} else if (x == x->parent->left) {
|
||||
x->parent->left = y;
|
||||
} else {
|
||||
x->parent->right = y;
|
||||
}
|
||||
y->left = x;
|
||||
x->parent = y;
|
||||
}
|
||||
|
||||
// rotate right at node x
|
||||
void TreeAllocator::rbt_rot_r(tree_block_t* x) {
|
||||
tree_block_t* y = x->left;
|
||||
x->left = y->right;
|
||||
if (y->right != NULL) {
|
||||
y->right->parent = x;
|
||||
}
|
||||
y->parent = x->parent;
|
||||
if (x->parent == nullptr) {
|
||||
this->free_start = y;
|
||||
} else if (x == x->parent->right) {
|
||||
x->parent->right = y;
|
||||
} else {
|
||||
x->parent->left = y;
|
||||
}
|
||||
y->right = x;
|
||||
x->parent = y;
|
||||
}
|
||||
|
||||
// find the node with the minimum key
|
||||
tree_block_t* TreeAllocator::rbt_minimum(tree_block_t* node) {
|
||||
while (node->left != NULL) {
|
||||
node = node->left;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
void TreeAllocator::rbt_remove(tree_block_t* z) {
|
||||
tree_block_t* x;
|
||||
tree_block_t* y;
|
||||
|
||||
y = z;
|
||||
bool y_original_red = y->red;
|
||||
if (z->left == NULL) {
|
||||
x = z->right;
|
||||
this->rbt_transplant(z, z->right);
|
||||
} else if (z->right == NULL) {
|
||||
x = z->left;
|
||||
this->rbt_transplant(z, z->left);
|
||||
} else {
|
||||
y = this->rbt_minimum(z->right);
|
||||
y_original_red = y->red;
|
||||
x = y->right;
|
||||
if (y->parent == z) {
|
||||
x->parent = y;
|
||||
} else {
|
||||
this->rbt_transplant(y, y->right);
|
||||
y->right = z->right;
|
||||
y->right->parent = y;
|
||||
}
|
||||
|
||||
this->rbt_transplant(z, y);
|
||||
y->left = z->left;
|
||||
y->left->parent = y;
|
||||
y->red = z->red;
|
||||
}
|
||||
if (!y_original_red) {
|
||||
this->rbt_fix_remove(x);
|
||||
}
|
||||
}
|
||||
|
||||
// fix the rb tree modified by the delete operation
|
||||
void TreeAllocator::rbt_fix_remove(tree_block_t* x) {
|
||||
tree_block_t* s;
|
||||
while (x != this->free_start && x->red == false) {
|
||||
if (x == x->parent->left) {
|
||||
s = x->parent->right;
|
||||
if (s->red) {
|
||||
// case 3.1
|
||||
s->red = false;
|
||||
x->parent->red = true;
|
||||
this->rbt_rot_l(x->parent);
|
||||
s = x->parent->right;
|
||||
}
|
||||
|
||||
if (!s->left->red && !s->right->red) {
|
||||
// case 3.2
|
||||
s->red = true;
|
||||
x = x->parent;
|
||||
} else {
|
||||
if (!s->right->red) {
|
||||
// case 3.3
|
||||
s->left->red = false;
|
||||
s->red = true;
|
||||
this->rbt_rot_r(s);
|
||||
s = x->parent->right;
|
||||
}
|
||||
|
||||
// case 3.4
|
||||
s->red = x->parent->red;
|
||||
x->parent->red = false;
|
||||
s->right->red = false;
|
||||
this->rbt_rot_l(x->parent);
|
||||
x = this->free_start;
|
||||
}
|
||||
} else {
|
||||
s = x->parent->left;
|
||||
if (s->red) {
|
||||
// case 3.1
|
||||
s->red = false;
|
||||
x->parent->red = true;
|
||||
this->rbt_rot_r(x->parent);
|
||||
s = x->parent->left;
|
||||
}
|
||||
|
||||
if (!s->right->red && !s->right->red) {
|
||||
// case 3.2
|
||||
s->red = true;
|
||||
x = x->parent;
|
||||
} else {
|
||||
if (!s->left->red) {
|
||||
// case 3.3
|
||||
s->right->red = false;
|
||||
s->red = true;
|
||||
this->rbt_rot_l(s);
|
||||
s = x->parent->left;
|
||||
}
|
||||
|
||||
// case 3.4
|
||||
s->red = x->parent->red;
|
||||
x->parent->red = false;
|
||||
s->left->red = false;
|
||||
this->rbt_rot_r(x->parent);
|
||||
x = this->free_start;
|
||||
}
|
||||
}
|
||||
}
|
||||
x->red = false;
|
||||
}
|
||||
// END copy from algorithmtutorprograms
|
||||
|
||||
tree_block_t* TreeAllocator::rbt_search_bestfit(tree_block_t* node, unsigned int req_size) {
|
||||
if (node == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (req_size < this->get_size(node)) {
|
||||
if (node->left != NULL && this->get_size(node->left) >= req_size) {
|
||||
return this->rbt_search_bestfit(node->left, req_size);
|
||||
}
|
||||
|
||||
return node;
|
||||
} else if (req_size > this->get_size(node)) {
|
||||
if (node->right != NULL && this->get_size(node->right) >= req_size) {
|
||||
return this->rbt_search_bestfit(node->right, req_size);
|
||||
}
|
||||
|
||||
// Block doesn't fit
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Perfect fit
|
||||
return node;
|
||||
}
|
||||
|
||||
// DLL code
|
||||
void TreeAllocator::dll_insert(list_block_t* previous, list_block_t* node) {
|
||||
previous->next->previous = node;
|
||||
node->next = previous->next;
|
||||
node->previous = previous;
|
||||
previous->next = node;
|
||||
}
|
||||
|
||||
void TreeAllocator::dll_remove(list_block_t* node) {
|
||||
node->previous->next = node->next;
|
||||
node->next->previous = node->previous;
|
||||
}
|
||||
Reference in New Issue
Block a user