日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux下C程序进程地址空间布局

發布時間:2025/3/15 linux 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux下C程序进程地址空间布局 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

我們在學習C程序開發時經常會遇到一些概念:代碼段、數據段、BSS段(Block Started by Symbol)?、堆(heap)和棧(stack)。先看一張教材上的示意圖(來源,《UNIX環境高級編程》一書),顯示了進程地址空間中典型的存儲區域分配情況。

???????????

從圖中可以看出:

  • 從低地址到高地址分別為:代碼段、(初始化)數據段、(未初始化)數據段(BSS)、堆、棧、命令行參數和環境變量
  • 堆向高內存地址生長
  • 棧向低內存地址生長

還經常看到下面這個圖(來源,不詳):

???????????????????????????????????????????????????????????????????

?

先看一段程序。

?

view plain
  • #include?<stdio.h>??
  • #include?<stdlib.h>??
  • ??
  • int?global_init_a=1;??
  • int?global_uninit_a;??
  • static?int?static_global_init_a=1;??
  • static?int?static_global_uninit_a;??
  • const?int?const_global_a=1;??
  • ??
  • int?global_init_b=1;??
  • int?global_uninit_b;??
  • static?int?static_global_init_b=1;??
  • static?int?static_global_uninit_b;??
  • const?int?const_global_b=1;??
  • /*上面全部為全局變量,main函數中的為局部變量*/??
  • int?main()??
  • {??
  • ????int?local_init_a=1;??
  • ????int?local_uninit_a;??
  • ????static?int?static_local_init_a=1;??
  • ????static?int?static_local_uninit_a;??
  • ????const?int?const_local_a=1;??
  • ??
  • ????int?local_init_b=1;??
  • ????int?local_uninit_b;??
  • ????static?int?static_local_init_b=1;??
  • ????static?int?static_local_uninit_b;??
  • ????const?int?const_local_b=1;??
  • ??
  • ????int?*?malloc_p_a;??
  • ????malloc_p_a=malloc(sizeof(int));??
  • ??
  • ????printf("\n?????????&global_init_a=%p?\t????????????
  • ?????????global_init_a=%d\n",&global_init_a,global_init_a);???
  • ??
  • ????printf("???????&global_uninit_a=%p?\t??????????
  • ????????global_uninit_a=%d\n",&global_uninit_a,global_uninit_a);??????
  • ??
  • ????printf("??&static_global_init_a=%p?\t?????
  • ????????static_global_init_a=%d\n",&static_global_init_a,static_global_init_a);??
  • ??????
  • ????printf("&static_global_uninit_a=%p?\t???
  • ????????static_global_uninit_a=%d\n",&static_global_uninit_a,static_global_uninit_a);??
  • ??????
  • ????printf("????????&const_global_a=%p?\t???????????
  • ????????const_global_a=%d\n",&const_global_a,const_global_a);?????
  • ??
  • ??????
  • ????printf("\n?????????&global_init_b=%p?\t????????????
  • ????????global_init_b=%d\n",&global_init_b,global_init_b);????
  • ??
  • ????printf("???????&global_uninit_b=%p?\t??????????
  • ????????global_uninit_b=%d\n",&global_uninit_b,global_uninit_b);??????
  • ??
  • ????printf("??&static_global_init_b=%p?\t?????
  • ????????static_global_init_b=%d\n",&static_global_init_b,static_global_init_b);??
  • ??????
  • ????printf("&static_global_uninit_b=%p?\t???
  • ????????static_global_uninit_b=%d\n",&static_global_uninit_b,static_global_uninit_b);??
  • ??????
  • ????printf("????????&const_global_b=%p?\t???????????
  • ????????const_global_b=%d\n",&const_global_b,const_global_b);??
  • ??
  • ??????????????????
  • ??
  • ????printf("\n??????????&local_init_a=%p?\t????????????
  • ????????local_init_a=%d\n",&local_init_a,local_init_a);???
  • ??
  • ????printf("????????&local_uninit_a=%p?\t??????????
  • ????????local_uninit_a=%d\n",&local_uninit_a,local_uninit_a);??
  • ??????
  • ????printf("???&static_local_init_a=%p?\t?????
  • ????????static_local_init_a=%d\n",&static_local_init_a,static_local_init_a);??
  • ??????
  • ????printf("?&static_local_uninit_a=%p?\t???
  • ????????static_local_uninit_a=%d\n",&static_local_uninit_a,static_local_uninit_a);????
  • ??
  • ????printf("?????????&const_local_a=%p?\t???????????
  • ????????const_local_a=%d\n",&const_local_a,const_local_a);????
  • ??
  • ??????
  • ????printf("\n??????????&local_init_b=%p?\t????????????
  • ????????local_init_b=%d\n",&local_init_b,local_init_b);???
  • ??
  • ????printf("????????&local_uninit_b=%p?\t??????????
  • ????????local_uninit_b=%d\n",&local_uninit_b,local_uninit_b);??
  • ??????
  • ????printf("???&static_local_init_b=%p?\t?????
  • ????????static_local_init_b=%d\n",&static_local_init_b,static_local_init_b);??
  • ??????
  • ????printf("?&static_local_uninit_b=%p?\t???
  • ????????static_local_uninit_b=%d\n",&static_local_uninit_b,static_local_uninit_b);????
  • ??
  • ????printf("?????????&const_local_b=%p?\t???????????
  • ????????const_local_b=%d\n",&const_local_b,const_local_b);??
  • ??
  • ??
  • ????printf("?????????????malloc_p_a=%p?\t?????????????
  • ????????*malloc_p_a=%d\n",malloc_p_a,*malloc_p_a);??
  • ??????
  • ????return?0;??
  • }??
  • ?

    下面是輸出結果。

    ???????????

    先仔細分析一下上面的輸出結果,看看能得出什么結論。貌似很難分析出來什么結果。好了我們繼續往下看吧。

    ?

    接下來,通過查看proc文件系統下的文件,看一下這個進程的真實內存分配情況。(我們需要在程序結束前加一個死循環,不讓進程結束,以便我們進一步分析)。

    ??????在return 0前,增加 while(1); 語句

    重新編譯后,運行程序,程序將進入死循環。

    ??????

    使用ps命令查看一下進程的pid

    ? #ps -aux | grep a.out

    查看/proc/2699/maps文件,這個文件顯示了進程在內存空間中各個區域的分配情況。

    ? #cat? /proc/2699/maps

    上面紅顏色標出的幾個區間是我們感興趣的區間:

    • 08048000-08049000? r-xp? 貌似是代碼段
    • 08049000-0804a000 r--p?? 暫時不清楚,看不出來
    • 0804a000-0804b000 rw-p? 貌似為數據段
    • 08a7e000-08a9f000? rw-p? 堆
    • bff73000-bff88000???? rw-p?? 棧???

    我們把這些數據與剛才的程序運行結果進行比較,看看有什么結論。

    ??????????????? &global_init_a=0x804a018?????? 全局初始化:數據段??????????? ? global_init_a=1
    ????????????&global_uninit_a=0x804a04c??????全局未初始化:數據段?????? ? ?global_uninit_a=0
    ???? &static_global_init_a=0x804a01c ?? ??全局靜態初始化:數據段? ?? ?static_global_init_a=1
    &static_global_uninit_a=0x804a038 ???? 全局靜態未初始化:數據段? ?? static_global_uninit_a=0
    ???????? ??? &const_global_a=0x80487c0 ?? ?全局只讀變量:?代碼段??????? const_global_a=1

    ???? ????????? ? &global_init_b=0x804a020???????全局初始化:數據段????? global_init_b=1
    ???? ??? ?? &global_uninit_b=0x804a048?????? ?全局未初始化:數據段????? global_uninit_b=0
    ??? ?&static_global_init_b=0x804a024????? ? 全局靜態初始化:數據段??? static_global_init_b=1
    &static_global_uninit_b=0x804a03c??????? 全局靜態未初始化:數據段?? static_global_uninit_b=0
    ???????? ?? &const_global_b=0x80487c4????????全局只讀變量:?代碼段??????? ???? const_global_b=1

    ???????????? ??? &local_init_a=0xbff8600c ?? ????? 局部初始化:棧???????????????????? local_init_a=1
    ??????? ???? &local_uninit_a=0xbff86008 ?? ?????局部未初始化:棧???????????????? local_uninit_a=134514459
    ???? &static_local_init_a=0x804a028 ?? ???? 局部靜態初始化:數據段????? static_local_init_a=1
    ?&static_local_uninit_a=0x804a040??????? 局部靜態未初始化:數據段??? ?static_local_uninit_a=0
    ?????????? ? &const_local_a=0xbff86004????????局部只讀變量:棧???? const_local_a=1

    ???? ????????? ?? &local_init_b=0xbff86000????????局部初始化:棧???????? ?local_init_b=1
    ?? ???????????? &local_uninit_b=0xbff85ffc?????????局部未初始化:棧?????? ?local_uninit_b=-1074241512
    ???? ?&static_local_init_b=0x804a02c??????? 局部靜態初始化:數據段????? static_local_init_b=1
    ?&static_local_uninit_b=0x804a044??????? 局部靜態未初始化:數據段????? static_local_uninit_b=0
    ?????????? ???? &const_local_b=0xbff85ff8????????局部只讀變量:棧?????? ?const_local_b=1


    ?????????????????????????? p_chars=0x80487c8????????字符串常量:代碼段????????? p_chars=abcdef
    ????????????? ????? malloc_p_a=0x8a7e008 ?? ????malloc動態分配:堆??????? *malloc_p_a=0

    通過以上分析我們暫時可以得到的結論如下,在進程的地址空間中

    • 數據段中存放:全局變量(初始化以及未初始化的)、靜態變量(全局的和局部的、初始化的以及未初始化的)
    • 代碼段中存放:全局只讀變量(const)、字符串常量
    • 堆中存放:動態分配的區域
    • 棧中存放:局部變量(初始化以及未初始化的,但不包含靜態變量)、局部只讀變量(const)

    這里我們沒有發現BSS段,但是我們將未初始化的數據按照地址進行排序看一下,可以發現一個規律。

    ??????????????? &global_init_a=0x804a018?????? 全局初始化:數據段??????????? ? global_init_a=1
    ??? &static_global_init_a=0x804a01c ?? ??全局靜態初始化:數據段? ?? ?static_global_init_a=1
    ??????????????? &global_init_b=0x804a020???????全局初始化:數據段????? global_init_b=1
    ????&static_global_init_b=0x804a024????? ? 全局靜態初始化:數據段??? static_global_init_b=1
    ???? ? &static_local_init_a=0x804a028 ?? ???? 局部靜態初始化:數據段????? static_local_init_a=1
    ? ?????&static_local_init_b=0x804a02c??????? 局部靜態初始化:數據段????? static_local_init_b=1

    &static_global_uninit_a=0x804a038 ???? 全局靜態未初始化:數據段? ?? static_global_uninit_a=0
    &static_global_uninit_b=0x804a03c??????? 全局靜態未初始化:數據段?? static_global_uninit_b=0
    ??&static_local_uninit_a=0x804a040??????? 局部靜態未初始化:數據段??? ?static_local_uninit_a=0
    ??&static_local_uninit_b=0x804a044??????? 局部靜態未初始化:數據段????? static_local_uninit_b=0
    ?????????? &global_uninit_b=0x804a048?????? ?全局未初始化:數據段????? global_uninit_b=0
    ????????????&global_uninit_a=0x804a04c??????全局未初始化:數據段?????? ? ?global_uninit_a=0


    ??? 這里可以發現,初始化的和未初始化的數據好像是分開存放的,因此我們可以猜測BSS段是存在的,只不過數據段是分為初始化和未初始化(即BSS段)的兩部分,他們在加載到進程地址空間時是合并為數據段了,在進程地址空間中沒有單獨分為一個區域。

    ??? 還有一個問題,靜態數據與非靜態數據是否是分開存放的呢?請讀者自行分析一下。

    ?

    ?接下來我們從程序的角度看一下,這寫存儲區域是如何分配的。首先我們先介紹一下ELF文件格式。

    ELF(Executable and Linkable Format )文件格式是一個開放標準,各種UNIX系統的可執行文件都采用ELF格式,它有三種不同的類型: –可重定位的目標文件(Relocatable,或者Object File) –可執行文件(Executable) –共享庫(Shared Object,或者Shared Library) 下圖為ELF文件的結構示意圖(來源,不詳):

    ?????????????????????????????????????

    ?

    一個程序編譯生成目標代碼文件(ELF文件)的過程如下,此圖引自《程序員的自我修養》一書的一個圖:

    ?????????????????????????????????

    可以通過readelf命令查看EFL文件的相關信息,例如 readelf? -a? a.out? ,我們只關心各個段的分配情況,因此我們使用以下命令:

    # readelf -S?a.out ?????????????????????

    ?將這里的內存布局與之前看到的程序的運行結果進行分析:

    ??????????????? &global_init_a=0x804a018?????? 全局初始化:數據段??????????? ? global_init_a=1
    ????????????&global_uninit_a=0x804a04c??????全局未初始化:BSS段?????? ? ?global_uninit_a=0
    ???? &static_global_init_a=0x804a01c ?? ??全局靜態初始化:數據段? ?? ?static_global_init_a=1
    &static_global_uninit_a=0x804a038 ???? 全局靜態未初始化:BSS段? ?? static_global_uninit_a=0
    ???????? ??? &const_global_a=0x80487c0 ?? ?全局只讀變量:?只讀數據段??????? const_global_a=1

    ???? ????????? ? &global_init_b=0x804a020???????全局初始化:數據段????? global_init_b=1
    ???? ??? ?? &global_uninit_b=0x804a048?????? ?全局未初始化:BSS段????? global_uninit_b=0
    ??? ?&static_global_init_b=0x804a024????? ? 全局靜態初始化:數據段??? static_global_init_b=1
    &static_global_uninit_b=0x804a03c??????? 全局靜態未初始化:BSS段?? static_global_uninit_b=0
    ???????? ?? &const_global_b=0x80487c4????????全局只讀變量:?只讀數據段???????????? const_global_b=1

    ???? &static_local_init_a=0x804a028 ?? ???? 局部靜態初始化:數據段????? static_local_init_a=1
    ?&static_local_uninit_a=0x804a040??????? 局部靜態未初始化:BSS段??? ?static_local_uninit_a=0

    ??????&static_local_init_b=0x804a02c??????? 局部靜態初始化:數據段????? static_local_init_b=1
    ?&static_local_uninit_b=0x804a044??????? 局部靜態未初始化:BSS段????? static_local_uninit_b=0

    ?????????????????????????? p_chars=0x80487c8????????字符串常量:只讀數據段????????? p_chars=abcdef
    ELF 文件一般包含以下幾個段 :

    • .text section:主要是編譯后的源碼指令,是只讀字段。
    • .data section :初始化后的非const的全局變量、局部static變量。
    • .bss:未初始化后的非const全局變量、局部static變量。
    • .rodata字段? 是存放只讀數據?

    分析到這以后,我們在和之前分析的結果對比一下,會發現確實存在BSS段,地址為0804a030?,大小為0x20,之前我們的程序中未初始化的的確存放在這個地址區間中了,只不過執行exec系統調用時,將這部分的數據初始化為0后,放到了進程地址空間的數據段中了,在進程地址空間中就沒有必要存在BSS段了,因此都稱做數據段。同理,.rodata字段也是與text段放在一起了。

    在ELF文件中,找不到局部非靜態變量和動態分配的內容。

    ?

    以上有很多地方的分析,我在網上基本找不到很明確的結論,很多教材上也沒有描述,只是我通過程序分析得出的結論,如有不妥之處,請指出,歡迎交流。

    總結

    以上是生活随笔為你收集整理的Linux下C程序进程地址空间布局的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。