malloc/free

id:higeponmalloc/free のことを日記で書いていたので、BayOS で使っているものを紹介します。といっても自前実装ではなく、MonaWiki/MonaDat にある Mona-20050620-MonaDat.tar.bz2 を改良して使っています。割り当てが早いらしいのと、なによりソースコードがすごく小さいのが採用の理由です。new / delete がこれだけで使えるようになりますので、C++ で OS を書きたいと思っている人におすすめです(アプリプログラマには関係ないですが、メモリの割り当てと解放は OS やライブリーの仕事なので、OS を書く人はこれも自前でやらないとインスタンス1つさえ生成できません)。あと、MonaWiki にある sms_gc を組み合わせるとすごくコンパクトな GC が作れるので、D言語Java などで OS を書きたいと思っている人にもおすすめです(笑)。ちなみに、メモリ割り当て時にゼロクリアしていないので、危険だとおもったら、g_km.allocate 後にゼロクリアして使ってください。

memory.h

#ifndef _MEMORY_H_
#define _MEMORY_H_

#include "types.h"

typedef struct MemoryInfo {
	dword total, free;
};

class Memory {
private:
	dword start, end, used, firstFree;

public:
	void init(dword size, dword end);
	void* allocate(dword size);
	void free(void* address);
	void get_status(MemoryInfo* minfo);

private:
	dword search_fit(dword size);
	bool check_address(dword ptr, dword* prev);
	void set_next(dword ptr, dword next);
	dword get_next(dword ptr);
};

// カーネルメモリ
extern Memory g_km;

//
// ここから下は C++言語の new / delete サポート
// および C言語の malloc / free サポート
//

void* operator new(size_t size);
void  operator delete(void* address);

#ifdef __cplusplus
extern "C" {
#endif

void* malloc(size_t size);
void free(void * address);

#ifdef __cplusplus
}
#endif

void __builtin_delete(void* address);
void* __builtin_new(size_t size);
void* __builtin_vec_new(size_t size);
void __builtin_vec_delete(void* address);
inline void* operator new(size_t, void* __p) { return __p; }

#endif

memory.cpp

#include "Memory.h"

class MemoryEntry {
public:
	int Size;
	dword Prev, Next;

	inline static void write_used(dword ptr, dword size) {
		*(dword*)ptr = *(dword*)(ptr + size - sizeof(dword)) = size;
	}

	inline static void write_free(dword ptr, dword size, dword prev, dword next) {
		dword* p = (dword*)ptr;
		*(p++) = *(dword*)(ptr + size - sizeof(dword)) = size | 0x80000000;
		*(p++) = prev;
		*p = next;
	}
	
	inline static void write_last(dword ptr, dword prev) {
		dword* p = (dword*)ptr;
		*(p++) = 0;
		*p = prev;
	}
};

void Memory::init(dword start, dword end)
{
	this->start = this->firstFree = start;
	this->end = end;
	this->used = 0;
	MemoryEntry::write_last(this->start, 0);
}

void* Memory::allocate(dword size)
{
	dword sz = (sizeof(dword) + size + sizeof(dword) + 15) & 0xfffffff0; // align16
	dword ptr = this->search_fit(sz);
	if (ptr == 0 || ptr + sz > this->end) return NULL;
	this->used += sz;

	MemoryEntry* h = (MemoryEntry*)ptr;
	dword sz_old = h->Size & 0x7fffffff;
	dword nptr = ptr + sz, prev = h->Prev, next = nptr;
	if (sz_old == 0) {
		MemoryEntry::write_last(nptr, prev);
	} else {
		if (sz < sz_old) {
			MemoryEntry::write_free(nptr, sz_old - sz, prev, h->Next);
			((MemoryEntry*)h->Next)->Prev = nptr;
		} else {
			next = h->Next;
			((MemoryEntry*)next)->Prev = prev;
		}
	}
	MemoryEntry::write_used(ptr, sz);
	this->set_next(prev, next);
	return (void*)(ptr + sizeof(dword));
}

dword Memory::search_fit(dword size)
{
	for (dword p = this->firstFree; p <= this->end; p = ((MemoryEntry*)p)->Next) {
		int sz = ((MemoryEntry*)p)->Size;
		if (sz == 0) {
			return p;
		} else if (sz > 0) {
			break;
		}
		sz &= 0x7fffffff;
		if (size <= (dword)sz) return p;
	}
	return 0;
}

void Memory::free(void* address)
{
	dword ptr = ((dword)address) - sizeof(dword), prev = 0;
	if (!this->check_address(ptr, &prev)) return;
	this->used -= ((MemoryEntry*)ptr)->Size;

	dword sz = *(int*)ptr, next = this->get_next(prev);
	if (ptr > this->start) {
		int psz = *(int*)(ptr - sizeof(int));
		if (psz < 0)
		{
			psz &= 0x7fffffff;
			ptr -= psz;
			sz  += psz;
			prev = ((MemoryEntry*)ptr)->Prev;
		}
	}
	dword nptr = ptr + sz, nsz = *(dword*)nptr;
	if (nsz < 0) {
		sz += nsz & 0x7fffffff;
		next = ((MemoryEntry*)nptr)->Next;
	}

	if (nsz == 0) {
		MemoryEntry::write_last(ptr, prev);
	} else {
		MemoryEntry::write_free(ptr, sz, prev, next);
		((MemoryEntry*)next)->Prev = ptr;
	}
	this->set_next(prev, ptr);
}

bool Memory::check_address(dword ptr, dword* prev)
{
	if (ptr < this->start || ptr > this->end) return false;

	int psz1 = *(int*)ptr;
	if (psz1 < 16 || (psz1 & 15) != 0 || ptr + psz1 > this->end) return false;

	int psz2 = *(int*)(ptr + psz1 - sizeof(int));
	if (psz1 != psz2) return false;

	for (dword p = this->firstFree; p <= ptr; p = ((MemoryEntry*)p)->Next) {
		if (((MemoryEntry*)p)->Size >= 0) return false;
		*prev = p;
	}
	return true;
}

void Memory::set_next(dword ptr, dword next)
{
	if (ptr == 0) {
		this->firstFree = next;
	} else {
		((MemoryEntry*)ptr)->Next = next;
	}
}

dword Memory::get_next(dword ptr)
{
	return ptr == 0 ? this->firstFree : ((MemoryEntry*)ptr)->Next;
}

void Memory::get_status(MemoryInfo* minfo)
{
	minfo->total = this->end - this->start;
	minfo->free  = minfo->total - this->used;
}

//
// ここから下は C++言語の new / delete サポート
// および C言語の malloc / free サポート
//

void* operator new(size_t size)
{
  void* p = g_km.allocate(size);
  return p;
}

void operator delete(void* address)
{
  g_km.free(address);
  return;
}

void* operator new[](size_t size)
{
  void* p = g_km.allocate(size);
  return p;
}

void operator delete[](void* address)
{
  g_km.free(address);
  return;
}

void* malloc(size_t size)
{
  void* p = g_km.allocate(size);
  return p;
}

void free(void * address)
{
  g_km.free(address);
  return;
}

使い方

#include "memory.h"

/* カーネルメモリの初期化 */
Memory g_km;
dword km_start = 0x000200000;
dword km_end   = 0x0007fffff;
g_km.init(km_start, km_end);

/* new / delete */
Test* test = new Test();
delete(test);

/* malloc / free */
char* buff = (char*)malloc(1024);
free(buff);