早期的計算機中,要執行乙個程式,會將這個程式全部裝入記憶體,也就是說程式訪問的記憶體位址都是實際的物理記憶體位址。當計算機同時執行多個程式時,必須保證程式用到的記憶體總量要小於計算機實際物理記憶體的大小。於,是有了以下的問題:
1.程序間的位址空間不隔離(直接整個執行在記憶體上,會導致程序間資料的相互影響)
2.記憶體使用率低(當記憶體空間不足以執行更多的程式時,不得不將某個在執行的程式的部分資料暫時拷貝到硬碟上,然後載入更多的程式)
3.程式執行的位址不確定(騰出了足夠的記憶體空間,但騰出和分配時是隨機的)
以上的問題衍生出變通的方法:分段
即增加乙個中間層:虛擬位址。作業系統經由虛擬位址對映到實際的物理記憶體位址。
則程式訪問的不再是實際的物理記憶體位址,而是虛擬位址。只要作業系統處理好虛擬位址到實體地址的對映,就能保證不同的程式最終訪問的記憶體位址處於不同的區域(隔離)。
32位的系統中是4gb虛擬記憶體,因為乙個指標長度是4位元組。定址能力是0x00000000 - 0xffffffff.
分段的方法解決了問題1和問題3。
但記憶體使用率低的問題仍然存在,因為程式仍然是整個的載入的。事實上,程式的執行有區域性性特點,在某個時間段內,程式只是訪問一小部分資料,即程式的大部分資料在乙個時間段內都不會被用到。
所以衍生出更小粒度的方法:分頁
即把位址空間等分成固定大小的頁,一般是4kb或4mb.分頁的思想是程式執行時用到哪頁就為哪頁分配記憶體,沒用到的頁暫時保留在硬碟中。當用到這些頁時再在實體地址空間中為這些頁分配記憶體,然後建立虛擬位址空間中的頁和剛分配的物理記憶體頁間的對映。
例項:乙個pe檔案(可執行檔案)就是一些編譯鏈結好的資料和指令的集合,它會被分成很多頁,在pe執行的過程中,它往記憶體中裝載的單位就是頁。作業系統先為該程式建立乙個4gb的程序虛擬位址空間(為了對映機制所需要的資料結構:頁表和頁目)。
當建立完虛擬位址空間所需要的資料結構後,程序開始讀取
pe檔案的第一頁。在
pe檔案的第一頁包含了
pe檔案頭和段表等資訊,程序根據檔案頭和段表等資訊,將
pe檔案中所有的段一一對映到虛擬位址空間中相應的頁
(pe檔案中的段的長度都是頁長的整數倍
)。這時
pe檔案的真正指令和資料還沒有被裝入記憶體中,作業系統只是根據
pe檔案的頭部等資訊建立了
pe檔案和程序虛擬位址空間中頁的對映關係。當
cpu要訪問程式中用到的某個虛擬位址時,當
cpu發現該位址並沒有相相關聯的實體地址時,
cpu認為該虛擬位址所在的頁面是個空頁面,
cpu會認為這是個頁錯誤
(page fault)
,cpu
也就知道了作業系統還未給該
pe頁面分配記憶體,
cpu會將控制權交還給作業系統。作業系統於是為該
pe頁面在物理空間中分配乙個頁面,然後再將這個物理頁面與虛擬空間中的虛擬頁面對映起來,然後將控制權再還給程序,程序從剛才發生頁錯誤的位置重新開始執行。由於此時已為
pe檔案的那個頁面分配了記憶體,所以就不會發生頁錯誤了。隨著程式的執行,頁錯誤會不斷地產生,作業系統也會為程序分配相應的物理頁面來滿足程序執行的需求。也就是說,先是依據pe檔案的頭部資訊來建立虛擬位址空間中頁與pe檔案的對映關係,然後由cpu和作業系統協作產生缺頁錯誤來建立虛擬位址空間和實際實體地址空間的對映。(cpu要執行哪頁時,才真正的形成完整的對映)
函式呼叫:
1.首先,將所有或一部分引數壓入棧中,如果有其他引數沒有入棧,那麼使用某些特定的暫存器傳遞。
3.跳轉到函式體執行。
4.指令call執行時,先把ebp壓入棧中(old ebp)。而後mov ebp,esp(即ebp指向棧頂,此時棧頂就是old ebp),再然後sub esp,***(在棧上分配臨時空間),再就是push ***(儲存暫存器)
呼叫慣例:
1、引數傳遞方式:從右至左的順序壓引數入棧(對於乙個foo(int m,int n )這樣乙個函式來說,引數入棧時的順序就是先n後m)
2、出棧方:由函式呼叫方將引數等出棧(因此在前面的函式呼叫收尾的**中,並沒有出現對於返回位址上面的引數部分的出棧工作,它是由呼叫方完成的,對於被呼叫函式來說對其進行任何操作)
棧記憶體與堆記憶體
可能許多人對記憶體分配上的 棧 stack 和 堆 heap 還不是很明白,包括一些科班出身的人也不明白這兩個概念。簡單來講,stack 上分配的記憶體系統自動釋放,heap 上分配的記憶體,系統不釋放,哪怕程式退出,那一塊記憶體還是在那裡。stack 一般是靜態分配記憶體,heap 上一般是動態分...
記憶體管理之棧stack
1 什麼是棧 棧是一種資料結構,c語言中使用棧來儲存區域性變數。棧是被發明出來管理記憶體的。2 棧管理記憶體的特點 小記憶體 自動化 先進後出 filo first in last out 棧 先進先出 fifo first in first out 佇列 棧的特點就是入口就是出口,只有乙個口,另乙...
堆記憶體和棧記憶體的管理
1 堆記憶體 堆記憶體是由程式設計師手工管理的,但它的申請是需要借助標準庫函式。在大小上,理論上是物理記憶體的大小。關於堆記憶體的資料儲存是靠程式設計師來管理的。由於是由程式設計師管理的,程式設計師的錯誤操作也導致記憶體的洩露和記憶體碎片的問題。關於堆記憶體的標準庫函式 stdlib.h mallo...