1. 程式人生 > >linux進程內存布局

linux進程內存布局

wid 變量的作用域 函數庫 log tel 功能 程序設計 tle 用戶

一個程序本質上都是由 BSS 段、data段、text段三個組成的。這樣的概念在當前的計算機程序設計中是很重要的一個基本概念,而且在嵌入式系統的設計中也非常重要,牽涉到嵌入式系統運行時的內存大小分配,存儲單元占用空間大小的問題。

  • BSS段:在采用段式內存管理的架構中,BSS段(bss segment)通常是指用來存放程序中未初始化的全局變量的一塊內存區域。BSS是英文Block Started by Symbol的簡稱。BSS段屬於靜態內存分配。
  • 數據段:在采用段式內存管理的架構中,數據段(data segment)通常是指用來存放程序中已初始化的全局變量的一塊內存區域。數據段屬於靜態內存分配。
  • 代碼段:在采用段式內存管理的架構中,代碼段(text segment)通常是指用來存放程序執行代碼的一塊內存區域。這部分區域的大小在程序運行前就已經確定,並且內存區域屬於只讀。在代碼段中,也有可能包含一些只讀的常數變量,例如字符串常量等。

程序編譯後生成的目標文件至少含有這三個段,這三個段的大致結構圖如下所示:

技術分享圖片

其中.text即為代碼段,為只讀。.bss段包含程序中未初始化的全局變量和static變量。data段包含三個部分:heap(堆)、stack(棧)和靜態數據區。

  • 堆(heap):堆是用於存放進程運行中被動態分配的內存段,它的大小並不固定,可動態擴張或縮減。當進程調用malloc等函數分配內存時,新分配的內存就被動態添加到堆上(堆被擴張);當利用free等函數釋放內存時,被釋放的內存從堆中被剔除(堆被縮減)
  • 棧 (stack):棧又稱堆棧, 是用戶存放程序臨時創建的局部變量,也就是說我們函數括弧“{}”中定義的變量(但不包括static聲明的變量,static意味著在數據段中存放變 量)。除此以外,在函數被調用時,其參數也會被壓入發起調用的進程棧中,並且待到調用結束後,函數的返回值也會被存放回棧中。由於棧的先進先出特點,所以 棧特別方便用來保存/恢復調用現場。從這個意義上講,我們可以把堆棧看成一個寄存、交換臨時數據的內存區。

當程序在執行時動態分配空間(C中的malloc函數),所分配的空間就屬於heap。其概念與數據結構中“堆”的概念不同。

stack段存放函數內部的變量、參數和返回地址,其在函數被調用時自動分配,訪問方式就是標準棧中的LIFO方式。(因為函數的局部變量存放在此,因此其訪問方式應該是棧指針加偏移的方式,否則若通過push、pop操作來訪問相當麻煩)

data段中的靜態數據區存放的是程序中已初始化的全局變量、靜態變量和常量。

在采用段式內存管理的架構中(比如intel的80x86系統),BSS 段(Block Started by Symbol segment)通常是指用來存放程序中未初始化的全局變量的一塊內存區域,一般在初始化時 BSS 段部分將會清零。BSS 段屬於靜態內存分配,即程序一開始就將其清零了。

比如,在C語言之類的程序編譯完成之後,已初始化的全局變量保存在.data 段中,未初始化的全局變量保存在.bss 段中。

text和data段都在可執行文件中(在嵌入式系統裏一般是固化在鏡像文件中),由系統從可執行文件中加載;而BSS段不在可執行文件中,由系統初始化。

技術分享圖片 圖引自《C專家編程》

BSS段只保存沒有值的變量,所以事實上它並不需要保存這些變量的映像。運行時所需要的BSS段大小記錄在目標文件中,但BSS段並不占據目標文件的任何空間。

技術分享圖片
    1. //main.c
    2. int a = 0; //全局初始化區
    3. char *p1; //全局未初始化區
    4. main()
    5. {
    6. static int c =0; //全局(靜態)初始化區
    7. int b; //棧
    8. char s[] = "abc"; //棧
    9. char *p2; //棧
    10. char *p3 = "123456"; //"123456\0"在常量區,p3在棧上。
    11. p1 = (char *)malloc(10);
    12. p2 = (char *)malloc(20); //分配得來得10和20字節的區域就在堆區。
    13. }

技術分享圖片

(圖出自:http://www.tenouk.com/Bufferoverflowc/Bufferoverflow1c.html)

技術分享圖片

(圖出自:APUE-2e, http://infohost.nmt.edu/~eweiss/222_book/222_book.html)

The computer program memory is organized into the following:

Code segment(text segment)
Data Segment
-- Data (rodata + rwdata)
-- BSS
-- Heap
Stack Segment

Data

The data area contains global and staticvariables used by the program that are initialized. This segment can be furtherclassified into initialized read-only (rodata) area and initialized read-writearea (rwdata).

BSS

The BSS segment also known as uninitialized datastarts at the end of the data segment and contains all uninitialized globalvariables and static variables that are initialized to zero by default.

Heap

The heap area begins at the end of the BSSsegment and grows to larger addresses from there. The heap area is managed bymalloc/calloc/realloc/new and free/delete, which may use the brk and sbrk system calls to adjust its size. The heaparea is shared by all shared libraries and dynamically loaded modules in aprocess.

Stack

The stack is a LIFO structure, typically locatedin the higher parts of memory. It usually "grows down" with everyregister, immediate value or stack frame being added to it. A stack frameconsists at minimum of a return address

例子程序

  1. //main.cpp
  2. int a = 0; // 全局初始化區(data)
  3. char *p1; // 全局未初始化區(bss)
  4. int main()
  5. {
  6. int b; // 棧區(stack)
  7. char s[] = "abc"; // 棧區(stack)
  8. char *p2; // 棧區(stack)
  9. char *p3 = "123456"; // p3 在棧區(stack); "123456\0" 在常量區(rodata)
  10. static int c =0; // 全局/靜態 初始化區 (data)
  11. p1 = (char *)malloc(10);
  12. p2 = (char *)malloc(20); // 分配得來的 10 和 20 字節的區域就在堆區 (heap)
  13. strcpy(p1, "123456"); // "123456\0" 放在常量區(rodata). 編譯器可能會將它與 p3 所指向的"123456\0"優化成一個地方。
  14. return 0;
  15. }

堆和棧的區別

管理方式:對於棧來講,是由編譯器自動管理;對於堆來說,釋放工作由程序員控制,容易產生 memory leak。

空間大小:一般來講在 32 位系統下,堆內存可以達到接近 4G 的空間,從這個角度來看堆內存幾乎是沒有什麽限制的。但是對於棧來講,一般都是有一定的空間大小的,例如,在 VC6 下面,默認的棧空間大小大約是 1M。

碎片問題:對於堆來講,頻繁的new/delete 勢必會造成內存空間的不連續,從而造成大量碎片,使程序效率降低;對於棧來講,則不會存在這個問題,因為棧是先進後出的隊列,永遠都不可能有一個內存塊從棧中間彈出。

生長方向:對於堆來講,生長方向是向上的,也就是向著內存地址增加的方向;對於棧來講,它的生長方向是向下的,是向著內存地址減小的方向增長。

分配方式:堆都是動態分配的,沒有靜態分配的堆;棧有 2 種分配方式:靜態分配和動態分配。靜態分配是編譯器完成的,比如局部變量的分配,動態分配由 alloca 函數進行分配,但是棧的動態分配和堆是不同的,它的動態分配是由編譯器進行釋放,不需要我們手工實現。

分配效率:棧是機器系統提供的數據結構,計算機會在底層分配專門的寄存器存放棧的地址,壓棧出棧都有專門的指令執行,這就決定了棧的效率比較高; 堆則是 C/C++函數庫提供的,它的機制是很復雜的,例如為了分配一塊內存,庫函數會按照一定的算法(具體的算法可以參考數據結構/操作系統)在堆內存中搜索可用的足夠大小的空間,如果沒有足夠大小的空間(可能是由於內存碎片太多),就有可能調用系統功能去增加程序數據段的內存空間,然後進行返回。顯然,堆的效率比棧要低得多。

無論是堆還是棧,都要防止越界現象的發生。

關於 Global 和 Static 類型的一點討論

1. static 全局變量與普通的全局變量有什麽區別 ?

全局變量(外部變量)的定義之前再冠以 static 就構成了靜態的全局變量。

全局變量本身就是靜態存儲方式, 靜態全局變量當然也是靜態存儲方式。 這兩者在存儲方式上並無不同。

這兩者的區別在於非靜態全局變量的作用域是整個源程序, 當一個源程序由多個源文件組成時,非靜態的全局變量在各個源文件中都是有效的。 而靜態全局變量則限制了其作用域, 即只在定義該變量的源文件內有效, 在同一源程序的其它源文件中不能使用它。

由於靜態全局變量的作用域局限於一個源文件內,只能為該源文件內的函數公用,因此可以避免在其它源文件中引起錯誤。

static 全局變量只初使化一次,防止在其他文件單元中被引用。

2. static 局部變量和普通局部變量有什麽區別 ?

把局部變量改變為靜態變量後是改變了它的存儲方式即改變了它的生存期。把全局變量改變為靜態變量後是改變了它的作用域,限制了它的使用範圍。

static 局部變量只被初始化一次,下一次依據上一次結果值。

3. static 函數與普通函數有什麽區別?

static 函數與普通函數作用域不同,僅在本文件。只在當前源文件中使用的函數應該說明為內部函數(static),內部函數應該在當前源文件中說明和定義。對於可在當前源文件以外使用的函數,應該在一個頭文件中說明,要使用這些函數的源文件要包含這個頭文件.static 函數在內存中只有一份(.data),普通函數在每個被調用中維持一份拷貝。

linux進程內存布局