简体   繁体   中英

how to implement a memory allocator

I'm trying to implement the freelist algorithm to allocate memory. The two functions I'm trying to write can be described as shown below.

// allocates a block of memory of at least size words and returns the address of that memory or 0 if no memory could be allocated.
int64_t *mymalloc(int64_t size)

// deallocates the memory stored at addr. the address will either be one allocated by mymalloc or the value 0.
void myfree(int64_t *addr)

The implementations of these functions should only use memory returned by the function pool(), whose signature is described below. Thus it cannot use the functions new, delete, malloc, calloc, realloc, etc.

// pool is a function that returns the address of a beginning // of a block of RAM that may be used for dynamic memory 
// allocation. The size of the pool in bytes is stored in the // first word, which can be assumed to be a multiple of 8. 
// When pool() is called, the first word isn't always overwritten with its size.
// Each word is an int64_t *, and so is 8 bytes.
// Assume this function works.
int64_t *pool();

I think defining some global variables like freelst, which points to the start of the freelst, may be helpful. It can be defined as

int64_t *freelst = pool();

I know that when allocating memory, there are some steps to follow:

  • The free list pointer should be updated accordingly.
  • The number of allocated blocks should be incremented.
  • The amount of memory allocated should be subtracted from the first word of the freelist, so that the first word always stores the size of memory available.
  • One needs to check if the current block of memory has been previously freed.

When deallocating memory, one needs to ensure addresses are inserted into the freelist in increasing order so that neighbours can be determined. If neighbours (which differ by 8) are free, they need to be merged, and as many times as necessary until no free neighbours are encountered to reduce fragmentation. Also, the second word of the freelst should be a pointer to the next word of the free

Below is some code I've come up with for this problem. It's incomplete, but the basic ideas are there.

#include <iostream>
#include <cstdint>
#include "pool.h" // place where pool is defined


const int NODE_SIZE = 8;

int64_t *freelst = pool();

int64_t *start_of_pool = freelst; // just keep this fixed I guess

// assume that the pool function works.

int64_t *mymalloc(int64_t size) {
  
  int64_t *currentBlock = freelst;
  while (currentBlock) {
    if (*currentBlock >= size) { // if the currentBlock is large enough, set it to this value (we're doing first fit).
       break;
    }
    currentBlock = currentBlock + 1; // since incrementing involves moving to the address that's one word past the current one.
  }
  // assuming we've found a large enough block, we now have to allocate it
  if (currentBlock == 0) {
    return 0; // I think this should occur because not enough memory was found
  }
  int64_t *prev_val = freelst; // save the previous value of the freelist
  freelst = freelst + 1 + *currentBlock; // assuming *currentBlock is the size of currentBlock.
  *freelst -= NODE_SIZE + *currentBlock; // update the size of the freelst here (though likely this was done incorrectly)
  return currentBlock + 1; // return address one word after currentBlock
  // is this all if we're trying to implement a linked list using raw pointers?
  // I don't think so, but I'm not sure what else to add.
}

void myfree(int64_t *p) {
    if (p == 0) { 
        return; // of course if we're freeing a nullptr, we should return 0.
    }
    // assume the freelst is already in ascending order of course.
    // sort the freelst in linear time by positioning the currentBlock into the right place.
    // the basic idea is to use insertion sort.
    // find where the address p is in the free list.
    // I think another method would be to update the prevBlock as the currentBlock is being updated.
    int64_t *currentBlock = freelst; 
    int64_t *prevBlock = freelst;
    while (currentBlock != 0 && currentBlock + 1 <= p) { // comparing addresses
        prevBlock = currentBlock; // so it's set to the previous block
        currentBlock = (int64_t *)*(currentBlock + 1); // set it to the next address
        // as a linked list, I'm thinking of doing something like:
        // prevBlock = currentBlock;
        // currentBlock = currentBlock->next;
    }
    // after exiting, either currentBlock = 0, in which case p is the largest address,
    // or currentBlock + 1 > p, so it's smaller than the current address.
    if (currentBlock == 0) { // then p is the largest address
        if ((int64_t *)*(prevBlock + 1) != currentBlock) throw std::invalid_argument("A likely error occurred as prevBlock + 1 != currentBlock.");
        *(prevBlock + 1) = (int64_t)p;
        *(p + 1) = 0;
        // p->next = 0
        // prevBlock->next = p;

    } else {
        if (prevBlock == currentBlock) { // in this case currentBlock was the start of the freelst
            int64_t *temp = (int64_t *)*(currentBlock + 1);
            *(prevBlock + 1) = (int64_t)p; // cast so it passes type-checking
            *(p + 1) = (int64_t)temp;
            // here I'm trying to mimic what's done for a linked list:
            // int64_t *temp = currentBlock->next;
            // prevBlock->next = p;
            // p->next = temp; 
        } else {
            *(prevBlock + 1) = (int64_t)p;
            *(p + 1) = (int64_t)currentBlock;
            // here's what I think might be the equivalent for a linked list:
            
            // prevBlock->next = p;
            // p->next = currentBlock;
        }
    }
    if (currentBlock != 0) { // if it not null
        if (currentBlock + 1 + *currentBlock == (int64_t *)(currentBlock + 1)) { // check if currentBlock is adjacent to prevBlock
            *currentBlock += *(int64_t *)*(currentBlock + 1) + NODE_SIZE;
        }
        // link current block to next next block
        *(currentBlock + 1) = (int64_t)((int64_t *)*(currentBlock + 1) + 1);
    }
    // assuming sorting was done correctly, check if addresses are adjacent
    if (prevBlock + 1 + *prevBlock == currentBlock) { // if you add one word plus the size of the previous block to get the
        // currentBlock
        if (currentBlock == 0) throw std::invalid_argument("A likely error occurred. currentBlock was 0 even though it should have been defined.");
        *prevBlock += *currentBlock + NODE_SIZE; // add the sizes of both the currentBlock and previous block,
        // assuming they aren't null of course.
        // so currentBlock->next->size + NODE_SIZE;
        // link previous block to next block
        *(prevBlock + 1) = (int64_t)(currentBlock + 1);

    }

}

Any help as to how to implement these functions/cases to consider that I've missed with code that deals with them would be appreciated. I can also clarify things if necessary.

I tried looking at this website for some help too, but I'm still having issues.

how to implement a memory allocator

At high level, there are essentially two ways to acquire memory for a custom allocator:

  • Allocate memory using an implementation defined way. The exact details depend on the target system, so first step is to find out what system you are targeting.
  • Or allocate memory using a standard way (standard allocator, new, malloc, static storage, ...)

Once you've acquired the memory, you need some data structure to keep track of memory that has been allocated through the allocator. You seem to have roughly described the "free list" structure, which is commonly used for this purpose.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM