Cにmalloc
とfree
を実装しようとしていますが、メモリを再利用する方法がわかりません。現在、次のようなstruct
があります。
typedef struct _mem_dictionary {
void *addr;
size_t size;
int freed;
} mem_dictionary;
私のmalloc
は次のようになります:
void *malloc(size_t size) {
void *return_ptr = sbrk(size);
if (dictionary == NULL)
dictionary = sbrk(1024 * sizeof(mem_dictionary));
dictionary[dictionary_ct].addr = return_ptr;
dictionary[dictionary_ct].size = size;
dictionary[dictionary_ct].freed = 1;
dictionary_ct++;
return return_ptr;
}
メモリを解放するときは、アドレスを0
としてマークします(これは、アドレスが解放されていることを示します)。私のmalloc
では、forループを使用して、配列内の値が0
と等しいことを確認し、そのアドレスにメモリを割り当てます。私はこれを実装する方法にちょっと混乱しています。
これを行う最も簡単な方法は、フリーブロックのリンクリストを保持することです。 malloc
で、リストが空でない場合は、要求を満たすのに十分な大きさのブロックを検索し、それを返します。リストが空の場合、またはそのようなブロックが見つからない場合は、sbrk
を呼び出して、オペレーティングシステムからメモリを割り当てます。 free
では、メモリチャンクを空きブロックのリストに追加するだけです。おまけとして、隣接する解放されたブロックをマージしようとすることができ、返すブロックを選択するためのポリシーを変更できます(最初の適合、最適適合、...)。リクエストよりも大きい場合は、ブロックを分割することもできます。
いくつかのサンプル実装(テストされておらず、明らかにスレッドセーフではありません。自己責任で使用してください):
_typedef struct free_block {
size_t size;
struct free_block* next;
} free_block;
static free_block free_block_list_head = { 0, 0 };
static const size_t overhead = sizeof(size_t);
static const size_t align_to = 16;
void* malloc(size_t size) {
size = (size + sizeof(size_t) + (align_to - 1)) & ~ (align_to - 1);
free_block* block = free_block_list_head.next;
free_block** head = &(free_block_list_head.next);
while (block != 0) {
if (block->size >= size) {
*head = block->next;
return ((char*)block) + sizeof(size_t);
}
head = &(block->next);
block = block->next;
}
block = (free_block*)sbrk(size);
block->size = size;
return ((char*)block) + sizeof(size_t);
}
void free(void* ptr) {
free_block* block = (free_block*)(((char*)ptr) - sizeof(size_t));
block->next = free_block_list_head.next;
free_block_list_head.next = block;
}
_
注:_(n + align_to - 1) & ~ (align_to - 1)
_は、n
をn
より大きい_align_to
_の最も近い倍数に丸めるトリックです。これは、_align_to
_が2の累乗であり、数値のバイナリ表現に依存している場合にのみ機能します。
_align_to
_が2の累乗の場合、ビットセットは1つだけであるため、_align_to - 1
_はすべての最下位ビットセットを持ちます(つまり、_align_to
_は000 ... 010の形式です)。 ..0、および_align_to - 1
_の形式は_000...001...1
_)です。つまり、~ (align_to - 1)
にはすべての上位ビットが設定され、下位ビットは設定されていません(つまり、_111...110...0
_の形式です)。したがって、x & ~ (align_to - 1)
は、x
の下位ビットをすべてゼロに設定し、_align_to
_の最も近い倍数に切り捨てます。
最後に、_align_to - 1
_をsize
に追加すると、_align_to
_の最も近い倍数に切り上げられます(ただし、size
が__align_to
_の倍数である場合は、size
を取得する必要があります)。
辞書エントリのsize
フィールドをゼロに設定したくない場合は、再利用するためにその情報が必要になります。代わりに、ブロックが解放されたときにのみ_freed=1
_を設定します。
sbrk()
への呼び出しが介在している可能性があるため、隣接するブロックを結合することはできません。必要なのは、解放された十分に大きいブロックを検索するfor
ループだけです。
_typedef struct _mem_dictionary
{
void *addr;
size_t size;
int freed;
} mem_dictionary;
void *malloc(size_t size)
{
void *return_ptr = NULL;
int i;
if (dictionary == NULL) {
dictionary = sbrk(1024 * sizeof(mem_dictionary));
memset(dictionary, 0, 1024 * sizeof(mem_dictionary));
}
for (i = 0; i < dictionary_ct; i++)
if (dictionary[i].size >= size
&& dictionary[i].freed)
{
dictionary[i].freed = 0;
return dictionary[i].addr;
}
return_ptr = sbrk(size);
dictionary[dictionary_ct].addr = return_ptr;
dictionary[dictionary_ct].size = size;
dictionary[dictionary_ct].freed = 0;
dictionary_ct++;
return return_ptr;
}
void free(void *ptr)
{
int i;
if (!dictionary)
return;
for (i = 0; i < dictionary_ct; i++ )
{
if (dictionary[i].addr == ptr)
{
dictionary[i].freed = 1;
return;
}
}
}
_
これは、malloc()
の優れた実装ではありません。実際、ほとんどのmalloc
/free
実装は、mallocによって返される各ブロックに小さなヘッダーを割り当てます。ヘッダーは、たとえば、返されたポインターよりも8バイト少ないのアドレスから始まる場合があります。これらのバイトには、ブロックを所有する_mem_dictionary
_エントリへのポインタを格納できます。これにより、O(N) free
での操作を回避できます。malloc()
でO(N)を回避できます解放されたブロックのプライオリティキューを実装することにより、ブロックサイズをインデックスとして、2項式heapの使用を検討してください。
私はシルヴァンの応答からコードを借りています。彼は、オーバーヘッドを計算するfree_block * iniのサイズの計算に失敗したようです。
全体として、コードはこのfree_blockを割り当てられたメモリのヘッダーとして付加することで機能します。 1.ユーザーがmallocを呼び出すと、mallocはこのヘッダーの直後のペイロードのアドレスを返します。 2. freeが呼び出されると、ブロックのヘッダーの開始アドレスが計算され(ブロックアドレスからヘッダーサイズを差し引くことにより)、それが空きブロックプールに追加されます。
typedef struct free_block {
size_t size;
struct free_block* next;
} free_block;
static free_block free_block_list_head = { 0, 0 };
// static const size_t overhead = sizeof(size_t);
static const size_t align_to = 16;
void* malloc(size_t size) {
size = (size + sizeof(free_block) + (align_to - 1)) & ~ (align_to - 1);
free_block* block = free_block_list_head.next;
free_block** head = &(free_block_list_head.next);
while (block != 0) {
if (block->size >= size) {
*head = block->next;
return ((char*)block) + sizeof(free_block);
}
head = &(block->next);
block = block->next;
}
block = (free_block*)sbrk(size);
block->size = size;
return ((char*)block) + sizeof(free_block);
}
void free(void* ptr) {
free_block* block = (free_block*)(((char*)ptr) - sizeof(free_block ));
block->next = free_block_list_head.next;
free_block_list_head.next = block;
}