#include "clib.h" #include "i386.h" #include "paging.h" #include "kmalloc.h" /* * allocazione dinamica di memoria nello spazio kernel condiviso. * l'allocazione e` veramente dinamica, nel senso che le pagine * principali sono allocate con valloc, percio` non consuma memoria. * * non so come ho fatto, ma le proc sono incredibilmente corte e semplici * e anche abbastanza efficienti, visto che la fusione dei blocchi la * faccio solo quando e` necessario, e non ad ogni kfree. * * ho fatto alcune prove e sembra funzionare bene [18/09/1997] * * [24/09/97] aggiunto kcalloc e krealloc, che pero` non e` molto * efficiente ;) */ #define FREE_NODE 0x12345678 #define USED_NODE 0x87654321 typedef struct _heap { struct _heap* next; /* nodo successivo */ struct _heap* prev; /* precedente */ dword magic; /* free/used */ dword size; /* dimensione COMPRESA sizeof(heap_node) */ } heap_node; static heap_node* main_node; static heap_node* node_limit; addr_t kmalloc(size_t size) { heap_node* node = main_node; bool prev_was_free = false; ASSERT(size != 0); size = (size + 7 + sizeof(heap_node)) & (~7); while (node && node < node_limit) { ASSERT(node->magic == FREE_NODE || node->magic == USED_NODE); if (prev_was_free && node->magic == FREE_NODE) { /* * fonde 2 nodi liberi consecutivi */ heap_node* prev = node->prev; heap_node* next = node->next; ASSERT(prev != NULL); prev->next = next; if (next) next->prev = prev; prev->size += node->size; node = prev; } if (node->magic == FREE_NODE) prev_was_free = true; if (prev_was_free && node->size >= size) { /* * trovato un nodo abbastanza grande */ if (node->size > size+sizeof(heap_node)+16) { /* * se il nodo e` troppo grande, dividilo */ heap_node* next; // marca il blocco successivo come libero next = (heap_node*)((u8*)node + size); next->prev = node; next->next = node->next; next->magic = FREE_NODE; next->size = node->size - size; node->next = next; node->size = size; } node->magic = USED_NODE; // kprintf("kmalloc ritorna %xh", (addr_t)node + sizeof(heap_node)); return (addr_t)node + sizeof(heap_node); } node = node->next; } WARNING("NULL from kmalloc\n"); return 0; } void kfree(addr_t addr) { heap_node* node; ASSERT(addr != NULL); node = (heap_node*)(addr - sizeof(heap_node)); ASSERT(node->magic == USED_NODE); node->magic = FREE_NODE; } addr_t kcalloc(size_t size) { addr_t a = kmalloc(size); if (a) { memset(a, 0, size); } return a; } addr_t krealloc(addr_t a, size_t size) { heap_node* node; size_t nsize; size_t asize; node = (heap_node*)(a - sizeof(heap_node)); nsize = node->size; if (node->magic != USED_NODE) goto _ret; asize = (size + 7 + sizeof(heap_node)) & (~7); if (asize > node->size+sizeof(heap_node)+16) { heap_node* next; /* diminuisco la memoria allocata */ node->size = asize; next = (heap_node*)((u8*)node + asize); next->size = node->size - asize; next->magic = FREE_NODE; if (node->next) { node->next->prev = next; } next->next = node->next; next->prev = node; node->next = next; return a; } if (node->size >= asize) return a; // caso stupido else { /* aumento la memoria allocata */ /* caso + frequente, e - efficiente .. */ addr_t na; na = kmalloc(size); if (na) { memcpy(na, a, nsize); memset(na+nsize, 0, size-nsize); kfree(a); } else { WARNING("return NULL"); } return a; } _ret: WARNING("return NULL"); return NULL; } /* * pagine da destinare all'heap. si puo` anche esagerare, tanto la * memoria e` veramente allocata solo quando serve. * 64K sono + che sufficienti. */ void kmalloc_startup(int npages) { main_node = valloc(npages); ASSERT(main_node != NULL); node_limit = (heap_node*)((u8*)main_node + npages*4096); main_node->size = npages*4096; main_node->prev = NULL; main_node->next = NULL; main_node->magic = FREE_NODE; }