web-dev-qa-db-ja.com

malloc実装?

Cにmallocfreeを実装しようとしていますが、メモリを再利用する方法がわかりません。現在、次のような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と等しいことを確認し、そのアドレスにメモリを割り当てます。私はこれを実装する方法にちょっと混乱しています。

28
user675257

これを行う最も簡単な方法は、フリーブロックのリンクリストを保持することです。 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)_は、nnより大きい_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を取得する必要があります)。

23

辞書エントリの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の使用を検討してください。

8
Heath Hunnicutt

私はシルヴァンの応答からコードを借りています。彼は、オーバーヘッドを計算する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;
}
5
Kingkong Jnr