Process Memory Mapping in C
When a C program runs, its memory is divided into distinct segments, each serving a specific purpose. This layout is called the process memory map. Understanding this is critical in embedded systems, where:
- RAM is limited
- Memory placement is often controlled manually
- Bugs like stack overflow or memory corruption are common
Typical Memory Layout
+------------------------+ High Address
| Stack |
|------------------------|
| Heap |
|------------------------|
| Uninitialized Data | (.bss)
|------------------------|
| Initialized Data | (.data)
|------------------------|
| Read-Only Data | (.rodata)
|------------------------|
| Code (Text) | (.text)
+------------------------+ Low Address
Memory Segments
1. Code Segment (.text)
Contains compiled machine instructions. Includes: functions and executable code. Typically read-only
Embedded Insight
- Stored in Flash/ROM
- Cannot be modified at runtime
2. Read-Only Data (.rodata)
Contains: string literals ("Hello") and const global variables
Example
const int max_val = 100;
Embedded Insight
- Stored in Flash
- Saves RAM
3. Initialized Data Segment (.data)
- Stores global and static variables with initial values
Example
int g = 10;static int s = 5;
Key Points
- Stored in RAM
- Initial values copied from Flash at startup
4. Uninitialized Data Segment (.bss)
Stores global and static variables without initialization
Example
int g_uninit;static int s_uninit;
Key Points
- Automatically initialized to 0
- Does NOT take space in program file (efficient)
5. Heap
Used for dynamic memory allocation
Functions: malloc() calloc() and free()
Characteristics
Grows upward; Managed manually
Embedded Insight
- Often avoided due to: fragmentation and unpredictability
6. Stack
Stores: local variables, function parameters and return addresses
Characteristics
Grows downward; Automatically managed
Embedded Insight
- Limited size → risk of overflow
- Avoid deep recursion
Memory Mapping in Embedded Systems
| Segment | Location |
|---|---|
| .text | Flash |
| .rodata | Flash |
| .data | RAM |
| .bss | RAM |
| Heap | RAM |
| Stack | RAM |
Example Program to Visualize Memory Segments
<stdio.h> <stdlib.h>// Global variablesint global_init = 100; // .dataint global_uninit; // .bssconst int global_const = 200; // .rodatavoid demo_function(){ int local_var = 10; // stack int *heap_var = (int*)malloc(sizeof(int)); // heap *heap_var = 50; printf("\nInside demo_function:\n"); printf("Address of local_var (stack): %p\n", &local_var); printf("Address of heap_var (heap): %p\n", heap_var); free(heap_var);}int main(){ static int static_var = 300; // .data printf("Process Memory Mapping Demo\n\n"); printf("Address of main (code/text): %p\n", main); printf("Address of demo_function (text): %p\n", demo_function); printf("\nGlobal / Static Segments:\n"); printf("global_init (.data): %p\n", &global_init); printf("global_uninit (.bss): %p\n", &global_uninit); printf("global_const (.rodata):%p\n", &global_const); printf("static_var (.data): %p\n", &static_var); demo_function(); return 0;}
Sample Output (Addresses Vary)
Process Memory Mapping DemoAddress of main (code/text): 0x4005d0
Address of demo_function (text): 0x400590Global / Static Segments:
global_init (.data): 0x601040
global_uninit (.bss): 0x601050
global_const (.rodata): 0x4006a0
static_var (.data): 0x601060Inside demo_function:
Address of local_var (stack): 0x7ffc1234
Address of heap_var (heap): 0x602010
Key Observations
- Code & constants → lower memory (Flash)
- Globals → middle region
- Heap grows upward
- Stack grows downward
- Heap & Stack may collide → crash
Common Issues
1. Stack Overflow
- Too many local variables
- Deep recursion
2. Memory Leak (Heap)
- Forgetting
free()
3. Fragmentation
- Frequent malloc/free cycles
A comparison of execution across multiple environments
| Concept | Bare Metal | RTOS | Linux (GPOS) |
|---|---|---|---|
| Execution Model | Single main() loop (superloop) or interrupt-driven | Multiple tasks/threads managed by scheduler | Multiple processes + threads managed by kernel |
| Stack | One global stack | One stack per task | One stack per thread (user + kernel stacks) |
| Scheduling | Manual (you control flow) | Priority-based, often preemptive real-time | Complex scheduler (CFS), fairness + throughput |
| Context Switch | Not applicable (unless interrupts) | Save/restore registers + switch stack pointer | Full context switch: registers, MMU state, address space |
| Heap | Optional, often avoided or simple allocator | RTOS-managed, thread-safe heap APIs | Fully managed (glibc), virtual memory backed |
| Isolation | None | None (tasks share memory) | Strong isolation via virtual memory (MMU) |