Skip to content

Instantly share code, notes, and snippets.

@ramntry
Created October 20, 2011 00:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ramntry/1300082 to your computer and use it in GitHub Desktop.
Save ramntry/1300082 to your computer and use it in GitHub Desktop.
My malloc and free on sbrk()
#include <stdio.h>
#include <unistd.h>
#include "malloc.v3.h"
// Установка охранника в начало пока пустой кучи
//
MemoryAllocator::MemoryAllocator()
{
oldBrk = sbrk(0); // сохраняем первоначальную позицию границы
start = (MemoryBlock*) sbrk(sizeof(MemoryBlock)); // двигаем границу секции .data (статики) на размер охранника
start->prev = NULL; // (пока это одновременно и начало кучи - потому prev пуст)
start->reserved = 1; // договоримся считать охранник маркером занятой области
start->size = 0; // договоримся идентифицировать охранник по нулевому размеру
}
MemoryAllocator::~MemoryAllocator()
{
brk(oldBrk);
}
// захват памяти в куче
//
void * MemoryAllocator::malloc(int volume)
{
MemoryBlock * curr = start;
int to_reserve = (volume + sizeof(int) - 1) / sizeof(int) * // Расчет необходимой памяти:
sizeof(int) + sizeof(MemoryBlock); // 1. Выравнивание по разрядности системы (int)
// 2. Резервирование места под маркер
while (curr->size && // пока не уткнулись в конец кучи (в охранник)
(curr->reserved || // и пока маркеры либо занятых областей
curr->size < to_reserve)) // либо слишком малых
curr = (MemoryBlock*) ((char*)curr + curr->size); // ищем дальше свободное место
// нашли свободное место нужного размера или уткнулись в охранник
// и разметили место, куда найденный маркер нужно будет передвинуть в связи с
// выделением части памяти
//
MemoryBlock * next = (MemoryBlock*) ((char*)curr + to_reserve);
if (curr->size) // если наш curr не охранник - то есть, нашли место в ранее
{ // освобожденной области кучи
if (curr->size > to_reserve + sizeof(MemoryBlock)) // передвигать маркер, а не просто
{ // помечать всю найденную свободную область занятой имеет смысл лишь тогда,
// когда остаток места способен вместить нечто большее, чем новый маркер
MemoryBlock * nextNext = getNext(curr); // этот маркер нужен, чтобы перенастроить его prev на новый маркер
nextNext->prev = next; // перенастраиваем...
next->reserved = 0; // помечаем остаток как свободный
next->size = curr->size - to_reserve; // размер - как разность размеров
curr->size = to_reserve; // а размер выделенного блока - как запрошенный объем с поправками
} // на технические потери
curr->reserved = 1; // в любом случае помечаем область занятой
next->prev = curr; // и настраиваем указатель
}
else
{ // если места не нашли
sbrk(to_reserve); // передвигаем программную границу кучи дальше на нужный объем
curr->size = to_reserve;
next->size = 0; // и настраиваем новый указатель
next->reserved = 1;
next->prev = curr;
}
return (void*) (curr + 1);
}
// освобождение ранее выделенной памяти
//
void MemoryAllocator::free(void * memory)
{
MemoryBlock * toFree = (MemoryBlock*)
((char*)memory - sizeof(MemoryBlock));
MemoryBlock * next = getNext(toFree);
// этот код должен сдвигать программную границу кучи назад
// при освобождении крайних блоков.
if (!next->size) // если рядом - край
{ // проверить, не нужно ли слить с блоком слева
if (toFree->prev && !toFree->prev->reserved)
{ // если нужно, то сдвигаем границу на оба блока
int t = toFree->prev->size;
toFree->prev->size = 0; // и делаем предыдущий маркер охранником
toFree->prev->reserved = 1;
sbrk(-(toFree->size + t));
}
else { // иначе тоже только для крайнего маркера
int t = toFree->size;
toFree->size = 0;
sbrk(-t);
}
return;
}
// если с обеих сторон блоки свободные
if (toFree->prev && !(next->reserved || toFree->prev->reserved))
{
MemoryBlock * nextNext = getNext(next);
toFree->prev->size += toFree->size + next->size; // пометить все как свободное
nextNext->prev = toFree->prev; // и верно перенастроить указатель
}
else if (!next->reserved)
{
MemoryBlock * nextNext = getNext(next);
toFree->reserved = 0;
toFree->size += next->size;
nextNext->prev = toFree;
}
else if (toFree->prev && !toFree->prev->reserved)
{
toFree->prev->size += toFree->size;
next->prev = toFree->prev;
}
else
toFree->reserved = 0;
}
void MemoryAllocator::out()
{
printf("[%p]: ", oldBrk);
int * end = (int*) sbrk(0);
int * intOldBrk = (int*) oldBrk;
int i = 0;
for (; intOldBrk != end; intOldBrk++, i++)
printf("%x ", *intOldBrk);
printf(" [size: %x]\n", i * sizeof(int) - sizeof(MemoryBlock));
}
int * MemoryAllocator::set(int size, int filler)
{
int * a = (int*) malloc(sizeof(int) * size);
for (int i = 0; i < size; i++)
a[i] = filler;
return a;
}
#pragma once
struct MemoryBlock // маркер блоков памяти в куче - является заголовком каждого блока -
{ // и занятого, и свободного, а также терминирующим маркером-охранником
MemoryBlock * prev; // указатель на предыдущий такой маркер
unsigned int reserved; // характер блока - свободный или занятый
unsigned int size; // размер структурированной области данных - размер размеченного
}; // участка вместе с последующим маркером - очередным или охранником
class MemoryAllocator
{
public:
MemoryAllocator();
~MemoryAllocator();
void * malloc(int volume);
void free(void * memory);
void out();
int * set(int size, int filler);
private:
MemoryBlock * start;
void * oldBrk;
inline MemoryBlock * getNext (MemoryBlock * block)
{ return (MemoryBlock*) ((char*)block + block->size); }
};
#include <iostream>
#include "malloc.v3.h"
using namespace std;
int main()
{
MemoryAllocator ma;
ma.out();
int * a = ma.set(8, 2); // создать memory_malloc'ом массив int[8] и забить его 2
ma.out();
int * b = ma.set(6, 3);
ma.out();
ma.free(a);
ma.out();
a = ma.set(4, 4);
ma.out();
int * c = ma.set(1, 9); // воткнем девяточку в единственное свободное окошко
ma.out();
ma.free(b);
ma.out();
ma.free(a);
ma.out();
ma.free(c); // полное слияние свободных областей
ma.out(); // память свободна
return 0;
}
#! /bin/sh
g++ simple_test.cpp malloc.v3.cpp -o simple_test
cat simple_test.cpp
./simple_test
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment