1. 程式人生 > >記憶體管理中的程式碼段、資料段,棧,堆

記憶體管理中的程式碼段、資料段,棧,堆

1.函式程式碼存放在程式碼段。宣告的類如果從未使用,則在編譯時,會優化掉,其成員函式不佔程式碼段空間。

全域性變數或靜態變數,放在資料段,
區域性變數放在棧中,
用new產生的物件放在堆中,

記憶體分為4段,棧區,堆區,程式碼區,全域性變數區

BSS段:BSS段(bss segment)通常是指用來存放程式中未初始化的全域性變數的一塊記憶體區域。
BSS是英文Block Started by Symbol的簡稱。BSS段屬於靜態記憶體分配。

2.程式碼段、資料段、棧是CPU級別的邏輯概念,堆是語言級別的邏輯概念

3.還有一個常量區,其中的內容不許修改。
常見的 char *p = "hello"; 這裡面的"hello"就儲存在常量區

4.如1樓所說,把程式碼段、資料段,棧,堆這些並列在一起不太合適
程式碼段、資料段、堆疊段,這是一個概念
堆、棧、全域性區、常量區,這是另一個概念

5.STACK(棧)臨時區域性
HEAP(堆)動態
RW(讀寫)全域性
RO(只讀)程式碼
Char* s=”Hello,World”; S中“H”存放在記憶體RO中且不能修改。

6.CPU暫存器:CPU暫存器,其實就是來控制程式碼段和資料段的指令及資料讀取的地方,當然,CPU也有自己存放資料的地方,那就是通用暫存器裡的資料暫存器,通常是EDX暫存器,C語言裡有個register,就是把資料放在這個暫存器裡,這樣讀取資料就相當的快了,因為不用去記憶體找,就省去了定址和傳送資料的時間開銷。他還有一些暫存器是用來指示當前程式碼段的位置、資料段的位置、堆疊段的位置等等(注意這裡存放的只是相應的程式碼或資料在記憶體中的地址,並不是實際的值,然後根據這個地址,通過地址匯流排和資料匯流排,去記憶體中獲取相應的值),不然在執行程式碼的時候,指令和資料從哪取呢?呵呵。。。他裡面還有標誌暫存器,用來標識一些狀態位,比如標識算術溢位呀等等。。。。。

————————————————————————————————————————————————————————————————

記憶體分段(筆記) 


在馮諾依曼的體系結構中必須有:程式碼段,堆疊段,資料段
因為馮氏結構,本質就是取址,執行的過程


編譯器和系統在為變數分配是從高地址開始分配的.
全域性變數和函式引數在記憶體中的儲存是由低地值到高地址的.
函式引數為什麼會放到堆區呢?
這是因為我們的函式是在程式執行中進行動態的呼叫的.
在函式的編譯階段根本無法確定他會呼叫幾次,會需要多少記憶體.
即使可以確定那時候就為變數分配好記憶體著實也是一種浪費。
所以編譯器為函式引數選擇動態的分配..即在每次呼叫函式時才為它動態的進行分配空間.


####################################################


記憶體分為4段,棧區,堆區,程式碼區,全域性變數區

BSS段:BSS段(bss segment)通常是指用來存放程式中未初始化的全域性變數的一塊記憶體區域。
BSS是英文Block Started by Symbol的簡稱。BSS段屬於靜態記憶體分配。

資料段:資料段(data segment)通常是指用來存放程式中已初始化的全域性變數的一塊記憶體區域。資料段屬於靜態記憶體分配。

程式碼段:程式碼段(code segment/text segment)通常是指用來存放程式執行程式碼的一塊記憶體區域。
這部分割槽域的大小在程式執行前就已經確定,並且記憶體區域通常屬於只讀, 某些架構也允許程式碼段為可寫,即允許修改程式。
在程式碼段中,也有可能包含一些只讀的常數變數,例如字串常量等。程式碼段是存放了程式程式碼的資料,
假如機器中有數個程序執行相同的一個程式,那麼它們就可以使用同一個程式碼段。

堆(heap):堆是用於存放程序執行中被動態分配的記憶體段,它的大小並不固定,
可動態擴張或縮減。當程序呼叫malloc等函式分配記憶體時,新分配的記憶體就被動態新增到堆上(堆被擴張);
當利用free等函式釋放記憶體時,被釋放的記憶體從堆中被剔除(堆被縮減)
棧(stack):棧又稱堆疊, 是使用者存放程式臨時建立的區域性變數,
也就是說我們函式括弧“{}”中定義的變數(但不包括static宣告的變數,static意味著在資料段中存放變數)。
除此以外,在函式被呼叫時,其引數也會被壓入發起呼叫的程序棧中,並且待到呼叫結束後,函式的返回值也會被存放回棧中。
由於棧的先進先出特點,所以棧特別方便用來儲存/恢復呼叫現場。從這個意義上講,我們可以把堆疊看成一個寄存、交換臨時資料的記憶體區。


(1)記憶體分段和記憶體分頁一樣都是一種記憶體管理技術,分段:許可權保護,分頁:虛擬記憶體.


(2)分段後,程式設計師可以定義自己的段,各段有獨立的地址空間,象程序的地址空間互相獨立一樣.


(3)同一個類的例項分配在一個段中,只有該類的方法可以訪問,如果其他類的方法去訪問,會因為段保護而出錯.可以從硬體上實現類的資料保護和隱藏

####################################################################

分段好處:

cpu中的段暫存器-------段址(base)和偏移值的上限(limit)。
段址:有效地址 中,如果有效地址大於limit,便會引發異常。這樣就可以限制程式不能範圍當前段外的資料,不能訪問其他程式的資料。
面向物件的好處:物件就是一塊連續的記憶體中的資料


暫存器是特殊形式的記憶體,嵌入到處理器內部。

         每個程序需要訪問記憶體中屬於自身的區域,因此,可將記憶體劃分成小的段,按需分發給程序。
暫存器用來儲存和跟蹤程序當前維護的段。偏移暫存器(Offset Registers)用來跟蹤關鍵的資料放在段中的位置。

       在程序被載入記憶體中時,基本上被分裂成許多小的節(section)。我們比較關注的是6個主要的節:

(1) .text 節

    .text 節基本上相當於二進位制可執行檔案的.text部分,它包含了完成程式任務的機器指令。
該節標記為只讀,如果發生寫操作,會造成segmentation fault。在程序最初被載入到記憶體中開始,該節的大小就被固定。

(2).data 節

.data節用來儲存初始化過的變數,如:int a =0 ; 該節的大小在執行時固定的。

(3).bss 節

棧下節(below stack section ,即.bss)用來儲存為初始化的變數,如:int a; 該節的大小在執行時固定的。

(4) 堆節

堆節(heap section)用來儲存動態分配的變數,位置從記憶體的低地址向高地址增長。記憶體的分配和釋放通過malloc() 和 free() 函式控制。

(5) 棧節

棧節(stack section)用來跟蹤函式呼叫(可能是遞迴的),在大多數系統上從記憶體的高地址向低地址增長。
同時,棧這種增長方式,導致了緩衝區溢位的可能性。

(6)環境/引數節

     環境/引數節(environment/arguments section)用來儲存系統環境變數的一份複製檔案,
程序在執行時可能需要。例如,執行中的程序,可以通過環境變數來訪問路徑、shell 名稱、主機名等資訊。
該節是可寫的,因此在格式串(format string)和緩衝區溢位(buffer overflow)攻擊中都可以使用該節。
另外,命令列引數也保持在該區域中。


################################################################################

以win32程式為例。
程式執行時,作業系統將exe檔案對映入記憶體。exe檔案格式為頭資料和各段資料組成。

頭資料說明了exe檔案的屬性和執行環境,段資料又分為資料段,程式碼段,資源段等,段的多少和位置由頭資料說明。
也就是說,不僅僅只是程式碼段和資料段。這些段由不同的編譯環境和編譯引數控制,由編譯器自動生成exe的段和檔案格式。
當作業系統執行exe時,會動態建立堆疊段,它是動態的,並且屬於作業系統執行環境。

也就是說,程式在記憶體的對映一個為exe檔案對映,包括資料段、程式碼段等它是不變的。
另一個為堆疊段,它是隨程式執行動態改變的。

1、編譯器把原始碼轉化成分立的目的碼(.o或者.obj)檔案,這些檔案中的程式碼已經是可執行的機器碼或者是中間程式碼。
但是其中變數等事物的地址只是一些符號。   
2、接下來是通過連結器處理這些目的碼,主要目的就是把分立的目的碼連線成一份完整的可執行程式碼,
並將其中的地址符號換成相對地址。如果這時候產生錯誤,我們就可以得到一份地址符號列表,而不是變數列表。   
3、執行程式的時候作業系統分配足夠的記憶體空間,建立好系統支撐結構後把二進位制可執行程式碼讀入記憶體中。
在讀入過程中記憶體首址就成了程式的“絕對地址”(實際上還是相對地址,不過是作業系統裡的相對地址了)。
於是絕對地址+相對地址(就是偏移量)就得到了變數的地址。   
因此,CS的值是由系統填入的,而其它S暫存器的值則是根據程式程式碼中附加的資訊計算後得到的
參考連結:

https://blog.csdn.net/YEYUANGEN/article/details/6766567