Memory bug fix

This commit is contained in:
boreddevnl 2026-03-01 00:52:25 +01:00
parent 8afb488539
commit a4e0a42042
18 changed files with 174 additions and 213 deletions

View file

@ -135,6 +135,7 @@ clean:
run: $(ISO_IMAGE) run: $(ISO_IMAGE)
qemu-system-x86_64 -m 2G -serial stdio -cdrom $< -boot d \ qemu-system-x86_64 -m 2G -serial stdio -cdrom $< -boot d \
-m 4G \
-audiodev coreaudio,id=audio0 -machine pcspk-audiodev=audio0 \ -audiodev coreaudio,id=audio0 -machine pcspk-audiodev=audio0 \
-netdev user,id=net0,hostfwd=udp::12345-:12345 -device e1000,netdev=net0 \ -netdev user,id=net0,hostfwd=udp::12345-:12345 -device e1000,netdev=net0 \
-vga std -global VGA.xres=1920 -global VGA.yres=1080 \ -vga std -global VGA.xres=1920 -global VGA.yres=1080 \

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
src/kernel/.DS_Store vendored

Binary file not shown.

View file

@ -286,18 +286,18 @@ static uint32_t parse_color(const char *s) {
} }
static void cmd_init_config_defaults(void) { static void cmd_init_config_defaults(void) {
shell_config.prompt_drive_color = 0xFFCCCCCC; // Default light gray shell_config.prompt_drive_color = 0xFFFFFFFF; // Default light gray
shell_config.prompt_colon_color = 0xFFCCCCCC; shell_config.prompt_colon_color = 0xFFCCCCCC;
shell_config.prompt_dir_color = 0xFF569CD6; // Blue shell_config.prompt_dir_color = 0xFF569CD6; // Blue
shell_config.prompt_op_color = 0xFFCCCCCC; shell_config.prompt_op_color = 0xFFCCCCCC;
shell_config.prompt_op_char = '>'; shell_config.prompt_op_char = '>';
shell_config.default_text_color = 0xFFCCCCCC; shell_config.default_text_color = 0xFFFFFFFF; // White
shell_config.bg_color = 0xFF1E1E1E; // Dark background shell_config.bg_color = 0xFF1E1E1E; // Dark background
shell_config.cursor_color = 0xFFFFFFFF; shell_config.cursor_color = 0xFFFFFFFF;
shell_config.show_drive = true; shell_config.show_drive = true;
shell_config.show_dir = true; shell_config.show_dir = true;
shell_config.dir_color = 0xFF569CD6; shell_config.dir_color = 0xFF569CD6;
shell_config.file_color = 0xFFCCCCCC; shell_config.file_color = 0xFFFFFFFF;
shell_config.size_color = 0xFF6A9955; // Green shell_config.size_color = 0xFF6A9955; // Green
shell_config.error_color = 0xFFFF4444; // Red shell_config.error_color = 0xFFFF4444; // Red
shell_config.success_color = 0xFF6A9955; shell_config.success_color = 0xFF6A9955;

View file

@ -9,6 +9,7 @@ static ipv4_address_t dns_result_ip;
static bool dns_resolved = false; static bool dns_resolved = false;
void dns_handle_response(void *data, uint16_t len) { void dns_handle_response(void *data, uint16_t len) {
(void)len;
dns_header_t *dns = (dns_header_t*)data; dns_header_t *dns = (dns_header_t*)data;
if ((ntohs(dns->flags) & 0x8000) == 0) return; // Not a response if ((ntohs(dns->flags) & 0x8000) == 0) return; // Not a response
@ -30,6 +31,9 @@ void dns_handle_response(void *data, uint16_t len) {
uint32_t ttl = ntohl(*(uint32_t*)p); p += 4; uint32_t ttl = ntohl(*(uint32_t*)p); p += 4;
uint16_t dlen = ntohs(*(uint16_t*)p); p += 2; uint16_t dlen = ntohs(*(uint16_t*)p); p += 2;
(void)class;
(void)ttl;
if (type == 1 && dlen == 4) { // A Record if (type == 1 && dlen == 4) { // A Record
dns_result_ip.bytes[0] = p[0]; dns_result_ip.bytes[0] = p[0];
dns_result_ip.bytes[1] = p[1]; dns_result_ip.bytes[1] = p[1];

View file

@ -114,42 +114,9 @@ void kmain(void) {
serial_write_hex(kernel_virt_base); serial_write_hex(kernel_virt_base);
serial_write("\n"); serial_write("\n");
uint64_t heap_phys_addr = 0;
size_t heap_size = 0;
if (memmap_request.response != NULL) { if (memmap_request.response != NULL) {
serial_write("[DEBUG] Memory Map entries: "); // The memory manager will now scan the memory map and manage all usable regions.
serial_write_num(memmap_request.response->entry_count); memory_manager_init_from_memmap(memmap_request.response);
serial_write("\n");
for (uint64_t i = 0; i < memmap_request.response->entry_count; i++) {
struct limine_memmap_entry *entry = memmap_request.response->entries[i];
serial_write("[DEBUG] Map entry ");
serial_write_num(i);
serial_write(": base=");
serial_write_hex(entry->base);
serial_write(" len=");
serial_write_hex(entry->length);
serial_write(" type=");
serial_write_num(entry->type);
serial_write("\n");
if (entry->type == LIMINE_MEMMAP_USABLE) {
if (entry->length > heap_size) {
heap_size = entry->length;
heap_phys_addr = entry->base;
}
}
}
}
if (heap_size > 512 * 1024 * 1024) heap_size = 512 * 1024 * 1024;
if (heap_phys_addr != 0) {
serial_write("[DEBUG] Selected heap base (Phys): 0x");
serial_write_hex(heap_phys_addr);
serial_write(", Size: ");
serial_write_num(heap_size / 1024 / 1024);
serial_write(" MB\n");
memory_manager_init_at((void*)p2v(heap_phys_addr), heap_size);
serial_write("[DEBUG] memory_manager_init OK\n"); serial_write("[DEBUG] memory_manager_init OK\n");
} else { } else {
serial_write("[DEBUG] ERROR: No usable memory for heap! Check Limine memmap.\n"); serial_write("[DEBUG] ERROR: No usable memory for heap! Check Limine memmap.\n");
@ -197,22 +164,13 @@ void kmain(void) {
for (uint64_t i = 0; i < module_request.response->module_count; i++) { for (uint64_t i = 0; i < module_request.response->module_count; i++) {
struct limine_file *mod = module_request.response->modules[i]; struct limine_file *mod = module_request.response->modules[i];
serial_write("[DEBUG] Found module: ");
serial_write(mod->path);
serial_write(" adr=0x");
serial_write_hex((uint64_t)mod->address);
serial_write(" size=");
serial_write_num(mod->size);
serial_write("\n");
const char *clean_path = mod->path; const char *clean_path = mod->path;
// Strip boot():/ or boot:/// prefixes common in different Limine versions // Strip boot():/ or boot:/// prefixes common in different Limine versions
if (fs_starts_with(clean_path, "boot():")) clean_path += 7; if (fs_starts_with(clean_path, "boot():")) clean_path += 7;
else if (fs_starts_with(clean_path, "boot:///")) clean_path += 8; else if (fs_starts_with(clean_path, "boot:///")) clean_path += 8;
serial_write("[DEBUG] Stripped module path: ");
serial_write(clean_path);
serial_write("\n");
FAT32_FileHandle *fh = fat32_open(clean_path, "w"); FAT32_FileHandle *fh = fat32_open(clean_path, "w");
if (fh && fh->valid) { if (fh && fh->valid) {

View file

@ -53,7 +53,7 @@ void create_man_entries(void) {
write_man_file("mv", "MV - Move or rename file\n\nUsage: mv <source> <dest>\n\nMoves or renames a file or directory."); write_man_file("mv", "MV - Move or rename file\n\nUsage: mv <source> <dest>\n\nMoves or renames a file or directory.");
write_man_file("cp", "CP - Copy file\n\nUsage: cp <source> <dest>\n\nCopies a file from the source path to the destination path."); write_man_file("cp", "CP - Copy file\n\nUsage: cp <source> <dest>\n\nCopies a file from the source path to the destination path.");
write_man_file("touch", "TOUCH - Create empty file\n\nUsage: touch <filename>\n\nCreates a new empty file if it doesn't exist."); write_man_file("touch", "TOUCH - Create empty file\n\nUsage: touch <filename>\n\nCreates a new empty file if it doesn't exist.");
write_man_file("cc", "CC - C Compiler\n\nUsage: cc <file.c>\n\nThe BoredOS C Compiler. Compiles C source files into ELF executables."); write_man_file("cc", "CC - C Compiler\n\nUsage: cc <file.c>\n\nThe BoredOS C Compiler. Compiles C source files into executables. (execute these with ./>file<)");
write_man_file("crash", "CRASH - Trigger kernel exception\n\nUsage: crash\n\nIntentionally triggers a null pointer dereference to test handlers."); write_man_file("crash", "CRASH - Trigger kernel exception\n\nUsage: crash\n\nIntentionally triggers a null pointer dereference to test handlers.");
write_man_file("boredver", "BOREDVER - Show OS version\n\nUsage: boredver\n\nDisplays current BoredOS version and kernel build information."); write_man_file("boredver", "BOREDVER - Show OS version\n\nUsage: boredver\n\nDisplays current BoredOS version and kernel build information.");
write_man_file("meminfo", "MEMINFO - Memory usage stats\n\nUsage: meminfo\n\nDisplays current physical and virtual memory allocation statistics."); write_man_file("meminfo", "MEMINFO - Memory usage stats\n\nUsage: meminfo\n\nDisplays current physical and virtual memory allocation statistics.");

View file

@ -2,11 +2,13 @@
// This software is released under the GNU General Public License v3.0. See LICENSE file for details. // This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms. // This header needs to maintain in any file it is present in, as per the GPL license terms.
#include "memory_manager.h" #include "memory_manager.h"
#include "io.h"
#include <stdint.h> #include <stdint.h>
#include "limine.h"
#include "platform.h"
// --- Internal State --- // --- Internal State ---
static uint8_t *memory_pool = NULL; // memory_pool is no longer a single pointer, as we now manage multiple regions.
// The block_list will contain all information about free and allocated regions.
static size_t memory_pool_size = 0; static size_t memory_pool_size = 0;
static MemBlock block_list[MAX_ALLOCATIONS]; static MemBlock block_list[MAX_ALLOCATIONS];
static int block_count = 0; static int block_count = 0;
@ -15,6 +17,9 @@ static size_t peak_allocated = 0;
static uint32_t allocation_counter = 0; static uint32_t allocation_counter = 0;
static bool initialized = false; static bool initialized = false;
extern void serial_write(const char *str);
extern void serial_write_num(uint32_t n);
// --- Helper Functions --- // --- Helper Functions ---
// Simple memset for internal use // Simple memset for internal use
@ -53,59 +58,8 @@ static uint32_t get_timestamp(void) {
return tick++; return tick++;
} }
// Find free space in memory pool with alignment // Sorts the block list by address. This is crucial for efficient merging of free blocks.
static void* find_free_space_aligned(size_t size, size_t alignment) { static void sort_block_list() {
size_t offset = 0;
// Ensure 8-byte minimum alignment for regular malloc if 0 is passed
if (alignment == 0) alignment = 8;
while (offset + size <= memory_pool_size) {
// Align offset
if ((uint64_t)((uint8_t*)memory_pool + offset) % alignment != 0) {
size_t diff = alignment - ((uint64_t)((uint8_t*)memory_pool + offset) % alignment);
offset += diff;
}
if (offset + size > memory_pool_size) break;
bool space_free = true;
// Check if this range is free
for (int i = 0; i < block_count; i++) {
if (!block_list[i].allocated) continue;
void *block_start = block_list[i].address;
void *block_end = (uint8_t *)block_start + block_list[i].size;
void *check_start = (uint8_t *)memory_pool + offset;
void *check_end = (uint8_t *)check_start + size;
// Check for overlap
if (check_start < block_end && check_end > block_start) {
space_free = false;
// Move offset past this block
offset = (size_t)((uint8_t *)block_end - (uint8_t *)memory_pool);
break;
}
}
if (space_free) {
return (uint8_t *)memory_pool + offset;
}
}
return NULL;
}
static void* find_free_space(size_t size) {
return find_free_space_aligned(size, 8);
}
// Calculate fragmentation
static size_t calculate_fragmentation(void) {
if (total_allocated == 0) return 0;
// Sort blocks by address
for (int i = 0; i < block_count - 1; i++) { for (int i = 0; i < block_count - 1; i++) {
for (int j = i + 1; j < block_count; j++) { for (int j = i + 1; j < block_count; j++) {
if ((uintptr_t)block_list[i].address > (uintptr_t)block_list[j].address) { if ((uintptr_t)block_list[i].address > (uintptr_t)block_list[j].address) {
@ -115,34 +69,31 @@ static size_t calculate_fragmentation(void) {
} }
} }
} }
// Count gaps between allocated blocks
size_t total_gaps = 0;
void *pool_end = (uint8_t *)memory_pool + memory_pool_size;
void *current_end = memory_pool;
for (int i = 0; i < block_count; i++) {
if (!block_list[i].allocated) continue;
if (block_list[i].address > current_end) {
total_gaps += (uintptr_t)block_list[i].address - (uintptr_t)current_end;
} }
current_end = (uint8_t *)block_list[i].address + block_list[i].size; // Calculate fragmentation
static size_t calculate_fragmentation(void) {
size_t total_free = memory_pool_size - total_allocated;
if (total_free == 0) return 0;
size_t largest_free = 0;
for (int i = 0; i < block_count; i++) {
if (!block_list[i].allocated && block_list[i].size > largest_free) {
largest_free = block_list[i].size;
}
} }
if (total_allocated == 0) return 0; if (total_allocated == 0) return 0;
return (total_gaps * 100) / total_allocated;
// Fragmentation = 1 - (Largest Free / Total Free)
size_t frag_percent = 100 - ((largest_free * 100) / total_free);
return frag_percent;
} }
// --- Public API --- // --- Public API ---
void memory_manager_init_at(void *pool_address, size_t pool_size) { void memory_manager_init_from_memmap(struct limine_memmap_response *memmap) {
if (initialized) return; if (initialized || !memmap) return;
memory_pool = (uint8_t *)pool_address;
memory_pool_size = pool_size;
// Clear metadata // Clear metadata
mem_memset(block_list, 0, sizeof(block_list)); mem_memset(block_list, 0, sizeof(block_list));
@ -150,114 +101,171 @@ void memory_manager_init_at(void *pool_address, size_t pool_size) {
total_allocated = 0; total_allocated = 0;
peak_allocated = 0; peak_allocated = 0;
allocation_counter = 0; allocation_counter = 0;
memory_pool_size = 0;
// Create initial free block representing entire pool for (uint64_t i = 0; i < memmap->entry_count; i++) {
block_list[0].address = memory_pool; struct limine_memmap_entry *entry = memmap->entries[i];
block_list[0].size = memory_pool_size;
block_list[0].allocated = false;
block_list[0].allocation_id = 0;
block_count = 1;
if (entry->type == LIMINE_MEMMAP_USABLE) {
uint64_t base = entry->base;
uint64_t size = entry->length;
// Avoid low memory below 1MB which is used for boot/kernel structures
if (base < 0x100000) {
if (base + size <= 0x100000) {
continue; // Skip this low memory block entirely
}
uint64_t diff = 0x100000 - base;
base = 0x100000;
size -= diff;
}
if (size < 4096) continue; // Ignore small fragments
if (block_count >= MAX_ALLOCATIONS) {
serial_write("[MEM] WARN: Exceeded MAX_ALLOCATIONS while parsing memmap.\n");
break;
}
block_list[block_count].address = (void*)p2v(base);
block_list[block_count].size = size;
block_list[block_count].allocated = false;
block_list[block_count].allocation_id = 0;
block_count++;
memory_pool_size += size;
}
}
sort_block_list();
initialized = true; initialized = true;
} serial_write("[MEM] Total usable memory: ");
serial_write_num(memory_pool_size / 1024 / 1024);
void memory_manager_init_with_size(size_t pool_size) { serial_write(" MB\n");
if (initialized) return;
}
void memory_manager_init(void) {
memory_manager_init_with_size(DEFAULT_POOL_SIZE);
} }
void* kmalloc_aligned(size_t size, size_t alignment) { void* kmalloc_aligned(size_t size, size_t alignment) {
if (!initialized) { if (!initialized || size == 0) return NULL;
memory_manager_init();
}
uint64_t rflags; uint64_t rflags;
asm volatile("pushfq; pop %0; cli" : "=r"(rflags)); asm volatile("pushfq; pop %0; cli" : "=r"(rflags));
if (size == 0 || size > memory_pool_size) { if (alignment == 0) alignment = 8;
asm volatile("push %0; popfq" : : "r"(rflags)); size = (size + 7) & ~7ULL; // Ensure size is multiple of 8
return NULL;
for (int i = 0; i < block_count; i++) {
if (block_list[i].allocated) continue;
uintptr_t block_start = (uintptr_t)block_list[i].address;
size_t block_size = block_list[i].size;
uintptr_t aligned_addr = block_start;
if (aligned_addr % alignment != 0) {
aligned_addr = (aligned_addr + alignment - 1) & ~(alignment - 1);
} }
if (total_allocated + size > memory_pool_size) { size_t padding = aligned_addr - block_start;
asm volatile("push %0; popfq" : : "r"(rflags));
return NULL; if (block_size >= size + padding) {
void* ptr = (void*)aligned_addr;
size_t remaining_size = block_size - (size + padding);
// The original free block becomes the trailing free part.
block_list[i].address = (void*)(aligned_addr + size);
block_list[i].size = remaining_size;
if (remaining_size == 0) {
for (int j = i; j < block_count - 1; j++) block_list[j] = block_list[j+1];
block_count--;
} }
// Find free space with alignment // Create a new block for the allocation.
void *ptr = find_free_space_aligned(size, alignment); if (block_count >= MAX_ALLOCATIONS) continue;
if (ptr == NULL) { block_list[block_count].address = ptr;
asm volatile("push %0; popfq" : : "r"(rflags)); block_list[block_count].size = size;
return NULL; block_list[block_count].allocated = true;
block_list[block_count].allocation_id = ++allocation_counter;
block_list[block_count].timestamp = get_timestamp();
block_count++;
// Create a new block for the leading padding if it exists.
if (padding > 0) {
if (block_count < MAX_ALLOCATIONS) {
block_list[block_count].address = (void*)block_start;
block_list[block_count].size = padding;
block_list[block_count].allocated = false;
block_count++;
}
} }
// Add block entry sort_block_list();
if (block_count >= MAX_ALLOCATIONS) {
asm volatile("push %0; popfq" : : "r"(rflags));
return NULL;
}
allocation_counter++;
int idx = block_count++;
block_list[idx].address = ptr;
block_list[idx].size = size;
block_list[idx].allocated = true;
block_list[idx].allocation_id = allocation_counter;
block_list[idx].timestamp = get_timestamp();
total_allocated += size; total_allocated += size;
if (total_allocated > peak_allocated) { if (total_allocated > peak_allocated) peak_allocated = total_allocated;
peak_allocated = total_allocated;
}
// Clear memory
mem_memset(ptr, 0, size); mem_memset(ptr, 0, size);
asm volatile("push %0; popfq" : : "r"(rflags)); asm volatile("push %0; popfq" : : "r"(rflags));
return ptr; return ptr;
} }
}
asm volatile("push %0; popfq" : : "r"(rflags));
return NULL;
}
void* kmalloc(size_t size) { void* kmalloc(size_t size) {
return kmalloc_aligned(size, 8); return kmalloc_aligned(size, 8);
} }
void kfree(void *ptr) { void kfree(void *ptr) {
if (ptr == NULL || !initialized) { if (ptr == NULL || !initialized) return;
return;
}
uint64_t rflags; uint64_t rflags;
asm volatile("pushfq; pop %0; cli" : "=r"(rflags)); asm volatile("pushfq; pop %0; cli" : "=r"(rflags));
// Find and free the block int block_idx = -1;
for (int i = 0; i < block_count; i++) { for (int i = 0; i < block_count; i++) {
if (block_list[i].allocated && block_list[i].address == ptr) { if (block_list[i].allocated && block_list[i].address == ptr) {
total_allocated -= block_list[i].size; block_idx = i;
block_list[i].allocated = false; break;
// Compact: remove freed entry and shift remaining
for (int j = i; j < block_count - 1; j++) {
block_list[j] = block_list[j + 1];
} }
}
if (block_idx == -1) {
asm volatile("push %0; popfq" : : "r"(rflags));
return;
}
total_allocated -= block_list[block_idx].size;
block_list[block_idx].allocated = false;
// Merge with next block if it's free and physically adjacent
if (block_idx + 1 < block_count && !block_list[block_idx + 1].allocated) {
uintptr_t current_end = (uintptr_t)block_list[block_idx].address + block_list[block_idx].size;
uintptr_t next_start = (uintptr_t)block_list[block_idx + 1].address;
if (current_end == next_start) {
block_list[block_idx].size += block_list[block_idx + 1].size;
for (int i = block_idx + 1; i < block_count - 1; i++) block_list[i] = block_list[i + 1];
block_count--;
}
}
// Merge with previous block if it's free and physically adjacent
if (block_idx > 0 && !block_list[block_idx - 1].allocated) {
uintptr_t prev_end = (uintptr_t)block_list[block_idx - 1].address + block_list[block_idx - 1].size;
uintptr_t current_start = (uintptr_t)block_list[block_idx].address;
if (prev_end == current_start) {
block_list[block_idx - 1].size += block_list[block_idx].size;
for (int i = block_idx; i < block_count - 1; i++) block_list[i] = block_list[i + 1];
block_count--; block_count--;
asm volatile("push %0; popfq" : : "r"(rflags)); asm volatile("push %0; popfq" : : "r"(rflags));
return; return;
} }
} }
asm volatile("push %0; popfq" : : "r"(rflags)); asm volatile("push %0; popfq" : : "r"(rflags));
} }
void* krealloc(void *ptr, size_t new_size) { void* krealloc(void *ptr, size_t new_size) {
if (!initialized) {
memory_manager_init();
}
if (new_size == 0) { if (new_size == 0) {
kfree(ptr); kfree(ptr);
return NULL; return NULL;
@ -267,24 +275,18 @@ void* krealloc(void *ptr, size_t new_size) {
return kmalloc(new_size); return kmalloc(new_size);
} }
// Find the block
for (int i = 0; i < block_count; i++) { for (int i = 0; i < block_count; i++) {
if (block_list[i].allocated && block_list[i].address == ptr) { if (block_list[i].allocated && block_list[i].address == ptr) {
if (block_list[i].size >= new_size) { if (block_list[i].size >= new_size) {
// Allocation is large enough
return ptr; return ptr;
} }
// Need to allocate new space
void *new_ptr = kmalloc(new_size); void *new_ptr = kmalloc(new_size);
if (new_ptr == NULL) { if (new_ptr == NULL) {
return NULL; return NULL;
} }
// Copy data
mem_memmove(new_ptr, ptr, block_list[i].size); mem_memmove(new_ptr, ptr, block_list[i].size);
// Free old pointer
kfree(ptr); kfree(ptr);
return new_ptr; return new_ptr;
@ -468,10 +470,7 @@ void memory_reset_peak(void) {
bool memory_is_valid_ptr(void *ptr) { bool memory_is_valid_ptr(void *ptr) {
if (ptr == NULL) return false; if (ptr == NULL) return false;
void *pool_start = memory_pool; if (!initialized) {
void *pool_end = (uint8_t *)memory_pool + memory_pool_size;
if (ptr < pool_start || ptr >= pool_end) {
return false; return false;
} }

View file

@ -7,6 +7,7 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include "limine.h"
// Memory Manager Configuration // Memory Manager Configuration
#define DEFAULT_POOL_SIZE (128 * 1024 * 1024) // 128MB default #define DEFAULT_POOL_SIZE (128 * 1024 * 1024) // 128MB default
@ -36,9 +37,7 @@ typedef struct {
} MemStats; } MemStats;
// Public API // Public API
void memory_manager_init(void); void memory_manager_init_from_memmap(struct limine_memmap_response *memmap);
void memory_manager_init_with_size(size_t pool_size);
void memory_manager_init_at(void *pool_address, size_t pool_size);
// Allocation/Deallocation // Allocation/Deallocation
void* kmalloc(size_t size); void* kmalloc(size_t size);