Commit 94d591e9 authored by Nikita Kuznetsov's avatar Nikita Kuznetsov
Browse files

complete lab (+tests)

parent 6b5c7aaa
......@@ -3,7 +3,7 @@ BUILDDIR=build
SRCDIR=src
CC=gcc
all: $(BUILDDIR)/mem.o $(BUILDDIR)/util.o $(BUILDDIR)/mem_debug.o
all: $(BUILDDIR)/mem.o $(BUILDDIR)/util.o $(BUILDDIR)/mem_debug.o $(BUILDDIR)/tests.o $(BUILDDIR)/main.o
$(CC) -o $(BUILDDIR)/main $^
build:
......@@ -18,6 +18,14 @@ $(BUILDDIR)/mem_debug.o: $(SRCDIR)/mem_debug.c build
$(BUILDDIR)/util.o: $(SRCDIR)/util.c build
$(CC) -c $(CFLAGS) $< -o $@
$(BUILDDIR)/tests.o: $(SRCDIR)/tests.c build
$(CC) -c $(CFLAGS) $< -o $@
$(BUILDDIR)/main.o: $(SRCDIR)/main.c build
$(CC) -c $(CFLAGS) $< -o $@
clean:
rm -rf $(BUILDDIR)
run:
./$(BUILDDIR)/main
#include <stdio.h>
#include <string.h>
#include "mem_internals.h"
#include "mem.h"
#include "tests.h"
int main() {
struct block_header* heap = (struct block_header*) heap_init(8175);
if (heap != NULL) {
test1(heap);
test2(heap);
test3(heap);
test4(heap);
fprintf(stdout, "All Tests passed\n");
} else {
fprintf(stdout, "Heap initialization failed\n");
}
return 0;
}
......@@ -32,15 +32,26 @@ static size_t region_actual_size( size_t query ) { return size_max( round_pages(
extern inline bool region_is_invalid( const struct region* r );
static void* map_pages(void const* addr, size_t length, int additional_flags) {
return mmap( (void*) addr, length, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | additional_flags , 0, 0 );
}
/* аллоцировать регион памяти и инициализировать его блоком */
static struct region alloc_region ( void const * addr, size_t query ) {
/* ??? */
static struct region alloc_region(void const* addr, size_t query) {
struct region allocated_region;
allocated_region.addr = map_pages(addr, query, MAP_FIXED);
size_t region_size = region_actual_size(query);
if (allocated_region.addr == MAP_FAILED || allocated_region.addr == NULL) {
allocated_region = (struct region) { map_pages(addr, query, 0), region_size, false };
} else {
allocated_region = (struct region) { allocated_region.addr, region_size, true };
}
block_init(allocated_region.addr, (block_size) { region_size }, NULL);
return allocated_region;
}
static void* block_after( struct block_header const* block ) ;
......@@ -56,22 +67,29 @@ void* heap_init( size_t initial ) {
/* --- Разделение блоков (если найденный свободный блок слишком большой )--- */
static bool block_splittable( struct block_header* restrict block, size_t query) {
static bool block_splittable(struct block_header* restrict block, size_t query) {
return block-> is_free && query + offsetof( struct block_header, contents ) + BLOCK_MIN_CAPACITY <= block->capacity.bytes;
}
static bool split_if_too_big( struct block_header* block, size_t query ) {
/* ??? */
}
static bool split_if_too_big(struct block_header* block, size_t query) {
if (block_splittable(block, query)) {
const block_capacity new_capacity = { .bytes = block->capacity.bytes - query };
  • Имея свободный блок block мы хотим взять из него query байт памяти. Вот тут надо понять, по какому адресу будет располагаться новый блок и какой у него будет размер. block это указатель на хедер. Можно даже картинку нарисовать.

    Edited by Даниил Садырин
  • Заметил ошибку в find_good_or_last, поправил (не отделялась память от свободных блоков, если это было возможно)

Please register or sign in to reply
block->capacity.bytes = query;
block_init(block_after(block), size_from_capacity(new_capacity), block->next);
block->next = block_after(block);
return true;
}
return false;
}
/* --- Слияние соседних свободных блоков --- */
static void* block_after( struct block_header const* block ) {
static void* block_after(struct block_header const* block) {
return (void*) (block->contents + block->capacity.bytes);
}
static bool blocks_continuous (
struct block_header const* fst,
static bool blocks_continuous (struct block_header const* fst,
struct block_header const* snd ) {
return (void*)snd == block_after(fst);
}
......@@ -80,10 +98,16 @@ static bool mergeable(struct block_header const* restrict fst, struct block_head
return fst->is_free && snd->is_free && blocks_continuous( fst, snd ) ;
}
static bool try_merge_with_next( struct block_header* block ) {
/* ??? */
}
static bool try_merge_with_next(struct block_header* block) {
if (block->next && mergeable(block, block->next)) {
block->capacity.bytes = block->capacity.bytes + size_from_capacity(block->next->capacity).bytes;
block->next = block->next->next;
return true;
}
return false;
}
/* --- ... ecли размера кучи хватает --- */
......@@ -92,34 +116,60 @@ struct block_search_result {
struct block_header* block;
};
static struct block_search_result find_good_or_last (struct block_header* restrict block, size_t sz) {
struct block_header* current_block = block;
while (current_block->next) {
while (try_merge_with_next(block));
if (current_block->is_free && block_is_big_enough(sz, current_block)) {
return (struct block_search_result){ .type = BSR_FOUND_GOOD_BLOCK, .block = current_block };
}
current_block = current_block->next;
}
static struct block_search_result find_good_or_last ( struct block_header* restrict block, size_t sz ) {
/*??? */
return (struct block_search_result) { .type=BSR_REACHED_END_NOT_FOUND, .block=current_block};
}
/* Попробовать выделить память в куче начиная с блока `block` не пытаясь расширить кучу
Можно переиспользовать как только кучу расширили. */
static struct block_search_result try_memalloc_existing ( size_t query, struct block_header* block ) {
}
struct block_search_result result = find_good_or_last(block, query);
if (result.type == BSR_FOUND_GOOD_BLOCK) {
split_if_too_big(result.block, query);
result.block->is_free = false;
}
return result;
}
static struct block_header *grow_heap(struct block_header* restrict last, size_t query) {
const struct region new_region = alloc_region(block_after(last), query);
  • после того как выделили новый регион памяти , не забыть смерджить его с блоком last. новый регион памяти ты вроде в alloc_region инициализируешь (через block_init)

Please register or sign in to reply
last->next = new_region.addr;
static struct block_header* grow_heap( struct block_header* restrict last, size_t query ) {
/* ??? */
return last;
}
/* Реализует основную логику malloc и возвращает заголовок выделенного блока */
static struct block_header* memalloc( size_t query, struct block_header* heap_start) {
/* ??? */
static struct block_header* memalloc(size_t query, struct block_header* heap_start) {
struct block_search_result result = try_memalloc_existing(query, heap_start);
if (result.type != BSR_FOUND_GOOD_BLOCK) {
result = try_memalloc_existing(query, grow_heap(result.block, query));
}
return result.block;
}
void* _malloc( size_t query ) {
struct block_header* const addr = memalloc( query, (struct block_header*) HEAP_START );
if (addr) return addr->contents;
else return NULL;
void* _malloc(size_t query) {
struct block_header* const addr = memalloc(query, (struct block_header*) HEAP_START);
if (addr) {
addr->is_free = false;
return addr->contents;
} else {
return NULL;
}
}
static struct block_header* block_get_header(void* contents) {
......@@ -127,8 +177,8 @@ static struct block_header* block_get_header(void* contents) {
}
void _free( void* mem ) {
if (!mem) return ;
struct block_header* header = block_get_header( mem );
if (!mem) return;
struct block_header* header = block_get_header(mem);
header->is_free = true;
/* ??? */
}
while(try_merge_with_next(header));
}
\ No newline at end of file
......@@ -32,7 +32,7 @@ void debug_block(struct block_header* b, const char* fmt, ... ) {
va_list args;
va_start (args, fmt);
vfprintf(stderr, fmt, args);
memalloc_debug_struct_info( stderr, b );
debug_struct_info( stderr, b );
va_end (args);
#else
......
#include <sys/mman.h>
#include <stdbool.h>
#include <stdio.h>
#include "util.h"
#include "mem.h"
#include "tests.h"
#include "mem_internals.h"
// void* heap;
static struct block_header* block_get_header(void* contents) {
return (struct block_header*) (((uint8_t*)contents) - offsetof(struct block_header, contents));
}
static size_t count_nonfree_blocks(struct block_header* heap) {
size_t res = 0;
while (heap) {
if (!heap->is_free) res++;
heap = heap->next;
}
return res;
}
// Проверка выделения памяти
void test1(struct block_header* heap) {
size_t block_size = 500;
printf("Test 1:\n");
printf("Allocating a single block\n");
void* block1 = _malloc(block_size);
if(block1 == NULL) fprintf(stdout, "Allocation failed\n");
struct block_header* block_header = block_get_header(block1);
if (block_header->is_free) fprintf(stdout, "Block should not be free\n");
if (block_header->capacity.bytes != block_size || heap->capacity.bytes != block_size) fprintf(stdout, "Block capacity set incorrectly\n");
_free(block1);
printf("\nTest 1 passed\n\n");
}
// Освобождение одного блока из нескольких выделенных
void test2(struct block_header* heap) {
size_t block_size1 = 200;
size_t block_size2 = 300;
printf("Test 2:\n");
printf("Freeing a single block from mutilpe blocks\n");
void* block1 = _malloc(block_size1);
if(block1 == NULL) fprintf(stdout, "Block 1 allocation failed\n");
void* block2 = _malloc(block_size2);
if(block2 == NULL) fprintf(stdout, "Block 2 allocation failed\n");
_free(block2);
struct block_header* block_header = block_get_header(block2);
if (!block_header->is_free) fprintf(stdout, "Block should be free\n");
if (count_nonfree_blocks(heap) != 1) fprintf(stdout, "Block 2 was not cleared correctly\n");
_free(block1);
printf("\nTest 2 passed\n\n");
}
// Освобождение двух блоков из нескольких выделенных
void test3(struct block_header* heap) {
size_t block_size1 = 200;
size_t block_size2 = 300;
size_t block_size3 = 400;
printf("Test 3:\n");
printf("Freeing two blocks from mutilpe blocks\n");
void* block1 = _malloc(block_size1);
if(block1 == NULL) fprintf(stdout, "Block 1 allocation failed\n");
void* block2 = _malloc(block_size2);
if(block2 == NULL) fprintf(stdout, "Block 2 allocation failed\n");
void* block3 = _malloc(block_size3);
if(block3 == NULL) fprintf(stdout, "Block 3 allocation failed\n");
_free(block3);
_free(block2);
struct block_header* block_header3 = block_get_header(block3);
if (!block_header3->is_free) fprintf(stdout, "Block 3 should be free\n");
struct block_header* block_header2 = block_get_header(block2);
if (!block_header2->is_free) fprintf(stdout, "Block 2 should be free\n");
if (count_nonfree_blocks(heap) != 1) fprintf(stdout, "Block 2 or/and 3 were not cleared correctly\n");
_free(block1);
printf("\nTest 3 passed\n\n");
}
// Память закончилась, новый регион памяти расширяет старый
void test4(struct block_header* heap) {
    • Нет 5 теста, где выделим память вручную за последним блоком хипа, при попытке расширить хип память выделится "где получится"
    • не используется MAP_FIXED_NOREPLACE в alloc_region
    • в тестах неплохо добавить debug_heap чтоб выводить хип
  • Спасибо, доделал и поправил

  • @nikita286532 перед 4 тестом хип такой:

     --- Heap ---
         start   capacity   status   contents
     0x4040000      16469     free   0000
     0x4044000       8175     free   0000

    ты пишиешь _malloc(10000); памяти хватает в хипе, поэтому расширения не происходит

    Edited by Даниил Садырин
Please register or sign in to reply
size_t block_size1 = 10000;
printf("Test 4:\n");
printf("Out of memory, extending region\n");
void* block1 = _malloc(block_size1);
if(block1 == NULL) fprintf(stdout, "Block 1 allocation failed\n");
if (count_nonfree_blocks(heap) != 1) fprintf(stdout, "Block extending region was not allocated correctly\n");
_free(block1);
printf("\nTest 4 passed\n\n");
}
\ No newline at end of file
#include "mem_internals.h"
void test1(struct block_header* heap);
void test2(struct block_header* heap);
void test3(struct block_header* heap);
void test4(struct block_header* heap);
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment