Memory Model

This article will describe what’s a memory model and what are different segments of the memory model used for. There is also a C memory management section to better associate the theory with reality.

Program Memory

https://upload.wikimedia.org/wikipedia/commons/thumb/5/50/Program_memory_layout.pdf/page1-225px-Program_memory_layout.pdf.jpg
The layout of a simple computer’s program memory with various sections

Text

The text segment, or code segment, or simply noted as “.text“, is where a portion of an object file or the corresponding section of the program’s virtual address space that containsthe executable code/instructions stored in. It’s generally read-only and fixed-size.

Data

The data segment contains any global or static variables which have a pre-defined value and can be modified. That is any variables that are not defined within a function (and thus can be accessed from anywhere) or are defined in a function but are defined as static so they retain their address across subsequent calls. Examples in C:

int val = 3;
char string[] = "Hello World";

The values for these variables are initially stored within the read-only memory (typically within .text) and are copied into the .data segment during the start-up routine of the program.

Note that if these variables had been declared from within a function, they would default to being stored in the local stack frame.

BSS

BSS (Block Started by Symbol) segment, also known as uninitialized data, is usually adjacent to the data segment.

The BSS segment contains all global variables and static variables that are initialized to zero or do not have explicit initialization in source code. For instance, a variable defined as static int i; would be contained in the BSS segment.

In C, statically-allocated objects without an explicit initializer are initialized to zero (for arithmetic types) or a null pointer (for pointer types).

Stack and heap

Stack

The stack (call stack/ program stack) is the memory set aside as scratch space for a thread of execution. When a function is called, a block is reserved on the top of the stack for local variables and some bookkeeping data.

A call stack is composed of stack frames (also called activation records or activation frames).

The stack frame usually includes at least the following items (in push order) :

  • the arguments (parameter values) passed to the routine (if any);
  • the return address back to the routine’s caller (e.g. in the DrawLine stack frame, an address into DrawSquare‘s code); and
  • space for the local variables of the routine (if any).
Call stack layout for upward-growing stacks

Heap

The heap is memory set aside for dynamic allocation.

The heap area is managed by malloc, calloc, realloc, and free (or in C++, new and delete, delete[] and free), which may use the brk and sbrk system calls to adjust its size.

Collisions

The stack area traditionally adjoined the heap area and they grew towards each other; when the stack pointer met the heap pointer, free memory was exhausted.

If that happens, you have hit the limit of your RAM, and need to find out ways to help ensure your program uses less memory.

Difference between Stack and Heap

The OS allocates the stack for each system-level thread when the thread is created. Typically the OS is called by the language runtime to allocate the heap for the application.

The stack is attached to a thread, so when the thread exits the stack is reclaimed. The heap is typically allocated at application startup by the runtime, and is reclaimed when the application (technically process) exits.

The size of the stack is set when a thread is created. The size of the heap is set on application startup, but can grow as space is needed (the allocator requests more memory from the operating system).

From Stack Overflow.

Microcontroller Memory

A basic overview of the memory model in Mbed OS:

+---------------------+   Last address of RAM
| Scheduler/ISR stack |
+---------------------+
|          ^          |
|          |          |
|                     |
|      Heap cont.     |
|---------------------|
| User thread n stack |
|---------------------|
| User thread 2 stack |
|---------------------|
| User thread 1 stack |
|---------------------|
|          ^          |
|          |          |
|                     |
|        Heap         |
+---------------------+
|                     |
| ZI: Global data     |
|                     |
+---------------------+
| ZI: Idle stack      |
+---------------------+
| ZI: Timer stack     |
+---------------------+
| ZI: Main stack      |
+---------------------+
|                     |
| ZI: Global data     |
|                     |
+---------------------+
|     Optional        |
|  Crash-Data-RAM     |
|      region         |
+---------------------+
| RW: Vector table    |
+=====================+   First address of RAM
|                     |   Last address of flash
|                     |
|     Application     |
|                     |
|                     |
+---------------------+
|                     |
| Optional bootloader |
|                     |
+---------------------+
| RO: Vector table    |
+---------------------+   First address of flash

The heap starts at the first address after the end of ZI, growing up into higher memory addresses, and the stack starts at the last memory address of RAM, and grows downwards into lower memory addresses.

RAM

This is the volatile memory that is the working data space for storing all variables whilst the program is running. In C, this is static variables, the heap, and the stack.

Inside RAM, you can distinguish two logical types: static and dynamic memory. Static memory is allocated at compile time and, consequently, does not change size during runtime. Dynamic memory is allocated at runtime.

Static

  • Vector table (read and write).
  • Crash data RAM.
  • Global data.
  • Static data.
  • Stacks for default threads (main, timer, idle and scheduler/ISR).

Dynamic

  • Heap (dynamic data).
  • Stacks for user threads. Mbed OS dynamically allocates memory on heap for user thread’s stacks.

Flash (ROM)

This is the non-volatile memory that primarily stores the program’s instructions, and also any “constant” data values. In general, you only read from this memory, and it is only written when you download new code to the mbed.

Flash is a read only memory (ROM) that contains:

  • Vector table (read only).
  • Application code.
  • Application data.
  • Optional bootloader.

The executable code, constants and other read-only data get put in a section called “RO” (for read-only), which is stored in the FLASH memory of the device.

Initialised static and global variables go into a section called “RW” (read-write), and the uninitialised ones in to one called “ZI” (Zero Initialise). RW and ZI need to live in RAM.

C Memory Model

The C runtime memory model can be divided in to three types: global/static memory, the heap, and the stack.

Static Memory

Global and static memory are values that are allocated for the entire lifetime of the program. It’s the space for the object is provided in the binary at compile-time. For example:

int x = 5;
int main() {}

Automatic Memory (Stack)

Stack is where temporary objects can be stored, and this space is automatically freed and reusable after the block in which they are declared is exited.

The Stack is used to store types of variables that have a fixed lifetime based on how C programs run. It is a section of RAM that grows and shrinks as the program runs. When you call a function, its parameters and any variables you have defined in that function (which are not static) are stored on the stack. For example:

void foo() {
    int x;
}
int main() {}

In this example, “x” will only exist for the duration of the call to foo().

Dynamic Memory (Heap)

Blocks of memory of arbitrary size can be requested at run-time using library functions such as malloc from a region of memory called the heap; these blocks persist until subsequently freed for reuse by calling the library function realloc or free.

The heap is used for dynamic memory allocation. When you create a new instance of an object using ‘new’, or if you allocate a block of memory using malloc and friends, you use memory in the heap. For example:

int *p;
int main() {
    p = new int;
}

When you use delete or free, the memory it was using inside the heap is deallocated, ready for use again. However, unless the memory you released was at the very end of the heap, the heap does not shrink.

Segmentation Fault

The following are some typical causes of a segmentation fault:

  • Attempting to access a nonexistent memory address (outside process’s address space)
  • Attempting to access memory the program does not have rights to (such as kernel structures in process context)
  • Attempting to write read-only memory (such as code segment)

These in turn are often caused by programming errors that result in invalid memory access:

  • Dereferencing a null pointer, which usually points to an address that’s not part of the process’s address space
  • Dereferencing or assigning to an uninitialized pointer (wild pointer, which points to a random memory address)
  • Dereferencing or assigning to a freed pointer (dangling pointer, which points to memory that has been freed/deallocated/deleted)
  • A buffer overflow
  • A stack overflow
  • Attempting to execute a program that does not compile correctly. (Some compilers will output an executable file despite the presence of compile-time errors.)

In C code, segmentation faults most often occur because of errors in pointer use, particularly in C dynamic memory allocation. Dereferencing a null pointer will always result in a segmentation fault, but wild pointers and dangling pointers point to memory that may or may not exist, and may or may not be readable or writable, and thus can result in transient bugs. For example:

char *p1 = NULL;  // Null pointer
char *p2;    // Wild pointer: not initialized at all.
// Initialized pointer to allocated memory
// (assuming malloc did not fail)
char *p3  = malloc(10 * sizeof(char));  
free(p3);   // p3 is now a dangling pointer, as memory has been freed

Reference

Data Segment: https://en.wikipedia.org/wiki/Data_segment

C Memory management: https://en.wikipedia.org/wiki/C_(programming_language)#Memory_management

Memory in C: https://craftofcoding.wordpress.com/2015/12/07/memory-in-c-the-stack-the-heap-and-static/

C Memort Model: http://www.cs.cornell.edu/courses/cs2022/2011sp/lectures/lect06.pdf

Mbed OS 5 Memory Model: https://os.mbed.com/docs/mbed-os/v5.13/reference/memory.html

Mbed OS 2 Memory Model: https://os.mbed.com/handbook/Memory-Model

Segmentation Fault: https://en.wikipedia.org/wiki/Segmentation_fault