If you have gone through any Operating System Course, you would have come across the Memory Layout Diagram of a Process in physical RAM. Below diagram show the layout of a process in the memory:
From this source code, let us try to predict where this variables would actually be getting stored in the memory.
We know that command line arguments and environment variable goes to the highest memory available to the process. Therefore, the address for this variables must be greater than any other variable or function address.
Next comes the stack. First function that is called in our program is main function itself which makes a call to func which in turn calls func2. Therefore, main should be at the bottom of the stack followed by func and func2 respectively. Mapping this stack into the memory, as stack grows from high memory to low memory, memory address of local variable of main should be greater than (but should be close to) local variable of func whereas func2's local variable should be having the lowest memory address among the three.
On the other hand, heap start from other side (lower address side) and grows toward stack. Therefore any memory allocated on heap should have address lower than any memory on stack. Our dynamic memory arr2 is allocated after arr1. Therefore, address of arr2 should be greater than arr1, as heap grow toward higher memory side.
Third section is data section which comprises of initialized and uninitialized data. Data section lies just below the heap. Hence, it address must be always less than any memory in heap or above heap. Also, uninitialized data is followed by initialized data. Therefore, address of any uninitialized data must be greater than that of initialized data. This behavior can be seen with the variable g1, g2, g3 and g4.
At last comes the text section which stores the read-only code. These falls at the bottom of the memory allocated to process and must be having address lower than any other section.
But, the question is how does these layout actually maps to a program that i have written?
Without an example with an actual running program, this concept seems vague and is pretty hard to visualize. I have written a C program to actually verify this layout in the memory. Here is the C code:
Without an example with an actual running program, this concept seems vague and is pretty hard to visualize. I have written a C program to actually verify this layout in the memory. Here is the C code:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include<stdio.h> | |
#include<stdlib.h> | |
#pragma GCC optimize ("O0") | |
// uninitialized variable | |
int g1; | |
int g2; | |
//iniatialized variable | |
int g3=5; | |
int g4=7; | |
// function to test stack | |
void func2() { | |
int var1; | |
int var2; | |
printf("On Stack through func2:\t\t %u %u\n",&var1,&var2); | |
} | |
void func() { | |
int var1; | |
int var2; | |
printf("On Stack through func:\t\t %u %u\n",&var1,&var2); | |
func2(); | |
} | |
int main(int argc, char* argv[], char* evnp[]) { | |
// Command line arguments | |
printf("Cmd Line and Env Var:\t\t %u %u %u\n",&argc,argv,evnp); | |
// Local variable will go to stack and stack should grow downward | |
int var1; | |
int var2; | |
printf("On Stack through main:\t\t %u %u\n",&var1,&var2); | |
func(); | |
// Dynamic Memory should go to heap and should be increasing | |
void *arr1 = malloc(5); | |
void *arr2 = malloc(5); | |
printf("Heap Data:\t\t\t\t\t %u\n",arr2); | |
printf("Heap Data:\t\t\t\t\t %u\n",arr1); | |
free(arr1); | |
free(arr2); | |
// Uninitialized and iniatialized Global Variable | |
printf("Global Uniniatialized:\t\t %u %u\n",&g1,&g2); | |
printf("Global iniatialized:\t\t %u %u\n",&g3,&g4); | |
//Static Code must go to Text section | |
printf("Text Data:\t\t\t\t\t %u %u ",main,func); | |
return 0; | |
} |
From this source code, let us try to predict where this variables would actually be getting stored in the memory.
We know that command line arguments and environment variable goes to the highest memory available to the process. Therefore, the address for this variables must be greater than any other variable or function address.
Next comes the stack. First function that is called in our program is main function itself which makes a call to func which in turn calls func2. Therefore, main should be at the bottom of the stack followed by func and func2 respectively. Mapping this stack into the memory, as stack grows from high memory to low memory, memory address of local variable of main should be greater than (but should be close to) local variable of func whereas func2's local variable should be having the lowest memory address among the three.
On the other hand, heap start from other side (lower address side) and grows toward stack. Therefore any memory allocated on heap should have address lower than any memory on stack. Our dynamic memory arr2 is allocated after arr1. Therefore, address of arr2 should be greater than arr1, as heap grow toward higher memory side.
Third section is data section which comprises of initialized and uninitialized data. Data section lies just below the heap. Hence, it address must be always less than any memory in heap or above heap. Also, uninitialized data is followed by initialized data. Therefore, address of any uninitialized data must be greater than that of initialized data. This behavior can be seen with the variable g1, g2, g3 and g4.
At last comes the text section which stores the read-only code. These falls at the bottom of the memory allocated to process and must be having address lower than any other section.
Here is the sample output of the program when run at ideone. Click on the link to see the run output at their site itself. I am pasting the same here:
You can verify that this conforms to above discussion. Here is a above memory layout diagram modified for our program:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Cmd Line and Env Var: 3216684080 3216684228 3216684236 | |
On Stack through main: 3216684052 3216684048 | |
On Stack through func: 3216684012 3216684008 | |
On Stack through func2: 3216683964 3216683960 | |
Heap Data: 144969752 | |
Heap Data: 144969736 | |
Global Uniniatialized: 134519328 134519332 | |
Global iniatialized: 134519320 134519316 | |
Text Data: 134513984 134513936 |
You can verify that this conforms to above discussion. Here is a above memory layout diagram modified for our program:
ab to ye program run kar ke dekhna he pare ga :) khoob mamu khoob
ReplyDeleteperfect explanation , thank you.
ReplyDeleteCouldn't find any better explanation of memory layout with such nyc example !!!
ReplyDeleteThnx a ton :)
Awesome work bro ..:) u explained very well !
ReplyDeleteNice explanation but i have a doubt.
ReplyDeleteif func2 have
static int a=4;
where it will be saved.
It will stored in the initialized data segment.
Deletewhere is volatile , register,and extern is saved.
ReplyDeleteVery well explained for creating a basic understanding. You could also mention about registers and how sometimes int, char or loop variables are stored in register for faster access due lack of CPU cycles.
ReplyDeleteGood explanation!
ReplyDeleteCan anyone tell why Stack segment is needed. I mean memory manager can have intelligence to know memory allocated to function calls and dynamically allopcated memory without having seperate sections for stack and heap.
ReplyDelete