6

Here is the context of my application : I'm working on an embedded system which uses RAM from different devices. One part in the internal RAM of the microcontroller (128kB) and the other part is the external RAM (1MB). These memories are mapped into the address space of the microcontroller, but in non contiguous regions.

The internal RAM is used for System stack, tasks stacks and Heap. The external RAM is used for statically allocated data (Pools, buffers, and all "static ..." stuff)

I am trying to implement a simple memory management structure, and as part of it be able to create an allocator which could use the allocation algorithm of operator new but using another memory source, not the system heap but a memory region elsewhere. Do you know if this is possible ?

An example of use could be to reserve 100kB of external RAM and to create an allocator to manage it, and then give it to a specified task which need this memory.

static const uint8_t* ramBase = reinterpret_cast<uint8_t*>(0x80000000);
static const uint32_t ramAreaSize = 0x19000; //100kB
BufferAllocator allocator(ramBase, ramAreaSize);

//...
//Assuming operator new is overloaded to use BufferAllocator
MyObject * obj = new (allocator) MyObject(some, parameter);
//...

The question is : how (if this is even possible) can I implement BufferAllocator in order to use operator new to manage the raw memory area ?

void* BufferAllocator::allocate(uint32_t bytes)
{
    //I would like to write something like this
    //and so let the responsibility to manage this memory area to "new"
    //so I don't have to reimplement (or reuse) a different custom  
    // allocator
    return ::operator new(ramBase, ramAreaSize, bytes)
} 
Inounx
  • 71
  • 6
  • 3
    Yes, it's possible, and that's the right syntax. You can't just use `delete` to free the objects though. Another alternative is to overload `operator new` and `operator delete` just for your `MyObject` type, so you don't need to use the placement new syntax. – Jonathan Wakely Apr 01 '15 at 16:40
  • This question: http://stackoverflow.com/questions/7194127/how-should-i-write-iso-c-standard-conformant-custom-new-and-delete-operators is a good place to start reading. – Dale Wilson Apr 01 '15 at 17:23
  • If you're going to use placement new use `->~()` for delete. – jiveturkey Apr 01 '15 at 17:37
  • I just figured out that my question is not really clear, so I edited it to add some details : the question is not about overloading operator new to do allocation using a custom allocator, but about the implementation of `BufferAllocator` to use the memory management already implemented for operator new – Inounx Apr 02 '15 at 08:00
  • Basically you're trying to implement your own heap over a fixed region of memory. If a block allocator (all blocks the same size) will do - search for that, that's been done before many times. – sharptooth Apr 02 '15 at 10:09
  • @sharptooth : Yes exactly, but before looking into a custom allocator to do that, i want to be sure there is no way I can do that with the existing code that "new" is using (i.e. the allocation management) – Inounx Apr 02 '15 at 11:48
  • @Inounx Suppose you're inside `operator new()` function and have to obtain memory from somewhere. The default implementation uses `malloc()`. You have to craft some magic over you fixed block - most likely a block allocator - and call that magic from inside `operator new()`. – sharptooth Apr 02 '15 at 12:21
  • CppUTest does change new and delete behavior, it is a nice reference for overloading new and delete. (https://github.com/cpputest/cpputest/blob/master/include/CppUTest/MemoryLeakDetectorNewMacros.h) – Felipe Lavratti Dec 18 '16 at 23:09

2 Answers2

1

I faced this same problem as well, and the only solution I could find was to write my own malloc and free. I didn't need anything special, so I just modeled my code after that shown in K&R's The C Programming Language (they have a naive example documented).

I then created two heaps: one for internal memory, and one for external memory. My stack is in an entirely different block of memory (CCRAM on an STM32F4), so I didn't need to worry about sbrk. However, I did have to know the start address of my internal SRAM heap based on the size of the data and bss segments. That was determined from extern symbols injected by the linker script.

I have enough information about my heap to know its current size, amount of free space, and whether there is enough contiguous memory to perform an allocation. If there's not enough in internal SRAM, it tries external SRAM. If there's not enough memory there, it's no different than the default malloc running out of memory.

I am using the GNU toolchain, so I was able to employ the --wrap option to override the C standard library's default malloc, free, realloc, and calloc (Actually malloc_r, free_r, realloc_r and calloc_r since I'm using newlib). Since new and delete eventually call malloc and friends, I was able to make it work (at least for my needs).

I'm not terribly confident with this approach, but it's the best I could do within the limitations of my abilities. Consider with scrutiny.

I would be interested in knowing a simpler solution.

Community
  • 1
  • 1
Verax
  • 2,409
  • 5
  • 27
  • 42
  • I think I will go for a similar solution than your's because it seems to not be possible to do what I want... I am working on a Cortex-M3 (EFM32GG) with the MDK ARM toolchain. The `-wrap` option is not a good solution i think as the expected behavior relies on the build command line and i intend to write this code as shareable between projects (it will be a part of a framework). Some interesting link from ARM here to extend the heap at runtime : [Extending heap size at runtime](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0475c/CHDFCIFC.html) – Inounx Apr 09 '15 at 07:59
1

There is no standard way to provide a memory area to operator new or malloc(). In POSIX-like systems, malloc() calls brk(), sbrk() and probably mmap() to get the area, so you might "capture" these calls and provide your own implementation, but, that is not portable (and a problem for mmap()).

Depending on your toolchain, you might be able to "explain" to your malloc() and/or operator new that it should handle two or more distinct memory areas, in some linker script or some such thing. But there is no guarantee.

Other than that, the only general solution that I can think of is to use a different general memory manager (like jemalloc) in your projects and find a way to configure it for your purpose.

srdjan.veljkovic
  • 2,468
  • 16
  • 24