Linux下的進程內存佈局 / c程序內存佈局

進程空間內存佈局

不管學習c語言還是學習linux下的編程,我們都要了解並學習。每個進程先天就有0-4G的各自互不干涉的虛擬內存空間,0-3G是用戶空間執行用戶自己的代碼,高1GB的空間是內核空間執行linux系統調用。這裏存放着整個內核的代碼和所有的內核模塊,用戶所看到和接觸的都是該虛擬地址,並不是實際的物理地址。linux下一個進程在內存裏有三部分的數據,就是“代碼段”、“堆棧段”和“數據段”。
下面我們來看一下linux下進程的內存佈局
在這裏插入圖片描述

上述圖片文字解析

1、棧(Stack):棧內存由編譯器在程序編譯階段完成,進程的棧空間位於進程用戶空間的頂部並且是向下增長,每個函數的每次調用都會在棧空間中開闢自己的棧空間,函數參數、局部變量、函數返回地址等都會按照先入者爲棧頂的順序壓入函數棧中,函數返回後該函數的棧空間消失,所以函數中返回局部變量的地址都是非法的。

2、堆(Heap):堆內存是在程序執行過程中分配的,用於存放進程運行中被動態分配的的變量,大小並不固定,堆位於非初始化數據段和棧之間,並且使用過程中是向棧空間靠近的。當進程調用 malloc 等函數分配內存時,新分配的內存並不是該函數的棧幀中,而是被動態添加到堆上,此時堆就向高地址擴張;當利用free 等函數釋放內存時,被釋放的內存從堆中被踢出,堆就會縮減。因爲動態分配的內存並不在函數棧幀中,所以即使函數返回這段內存也是不會消失。

3、未初始化數據段(.bss):用來存放未初始化的全局變量和 static 靜態變量。並且在程序開始執行之前,就是在 main()之前,內核會將此段中的數據初始化爲 0 或空指針。

4、已初始化數據段(.data): 通常將已初始化數據是在程序中聲明,並且具有初值的變量,這些變量需要佔用存儲器的空間,在程序執行時它們需要位於可讀寫的內存區域內,並具有初值,以供程序運行時讀寫。一般用來已初始化的全局變量和 static 靜態變量。

5、文本段也稱代碼段:這是可執行文件中由 CPU 執行的機器指令部分。正文段常常是隻讀的,以防止程序由於意外而修改其自身的執行。

內存佈局編程代碼

接下來我們編寫並運行下面C程序,驗證一下C程序編譯以及運行時的進程內存佈局:
我是用linux下vim下寫的編程!

#include <stdio.h>
#include <stdlib.h>

int g_var1; //g_var1是爲未初始化的全局變量,存放在數據段的BSS區,其值默認爲0;

int g_var2; //g_var2是初始化了的全局變量,存放在數據段的DATA區,其值爲初始化值20

int main(int argc, char **argv)
{
    static int s_var1;//s_var1是未初始化的靜態變量 ,存放在數據段的BSS區,其默認值爲0

    static int s_var2=10;//s_var2是初始化的靜態變量,存放在數據段的DATA區,其值爲初始化值10

    char   *str="hello";//str是初始化了的局部變量,存放在棧(STACK)中,其值是"Hello"這個字符串常量存放在DATA段裏RODATA區中的地址

    char   *ptr; //ptr是未初始化了的局部變量,存放在棧中,其值爲隨機值,這時候的ptr稱爲“野指針(爲初始化的指針)"

    ptr = malloc(100); // malloc()會從堆(HEAP)中分配100個字節的內存空間,並將該內存空間的首地址返回給ptr存放;


    printf("[cmd args]: argv address: %p\n", argv);
    printf("\n");
    printf("[ Stack]: str address: %p\n", &str);
    printf("[ Stack]: ptr address: %p\n", &ptr);
    printf("\n");
    printf("[ Heap ]: malloc address: %p\n", ptr);
    printf("\n");
    printf("[ bss ]: s_var1 address: %p value: %d\n", &s_var1, g_var1);
    printf("[ bss ]: g_var1 address: %p value: %d\n", &g_var1, g_var1);
    printf("[ data ]: g_var2 address: %p value: %d\n", &g_var2, g_var2);
    printf("[ data ]: s_var2 address: %p value: %d\n", &s_var2, s_var2);
    printf("[rodata]: \"%s\" address: %p \n", str, str);
    printf("\n");
    printf("[ text ]: main() address: %p\n", main);
    printf("\n");
    
    return 0;
}

代碼運行結果
在這裏插入圖片描述
結果分析:通過上面代碼的地址,我們可以發現,在內存中,從低地址向高地址,依次是文本段、文字常量段、已初始化數據段、未初始化數據段、堆區域和棧區域。(對照上面內存佈局圖片看)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章