C 虛擬位址空間

2021-09-27 04:16:05 字數 4138 閱讀 1548

一、引言:

在計算機中,每台裝置以及程序都被分配乙個位址空間。位址空間包括物理空間以及虛擬空間。如果將實體地址暴露給程序,使用者就很容易破壞作業系統,從而使系統停止;另外隨著程序數量和體積的增長,核心空間變得越來越不夠用,從而引入虛擬位址空間。

實體地址(physical address):放在定址匯流排上的位址。物理記憶體是以位元組為單位編址的。

虛擬位址(virtual address):cpu啟動保護模式後,程式執行在虛擬位址空間。cpu在啟動時是執行在實模式的,核心在初始化頁表之前並不是使用虛擬位址,而是直接使用實體地址。

三、虛擬位址空間展示

虛擬位址空間(virtual address space),在32位模式下它是乙個4gb的記憶體位址塊。在linux系統中, 核心程序和使用者程序所佔的虛擬記憶體比例是1:3,而windows系統為2:2(通過設定large-address-aware executables標誌也可為1:3)。這並不意味著核心使用那麼多物理記憶體,僅表示它可支配這部分位址空間,根據需要將其對映到物理記憶體。

虛擬位址通過頁表(page table)對映到物理記憶體,頁表由作業系統維護並被處理器引用。核心空間在頁表中擁有較高特權級,因此使用者態程式試圖訪問這些頁時會導致乙個頁錯誤(page fault)。在linux中,核心空間是持續存在的,並且在所有程序中都對映到同樣的物理記憶體。核心**和資料總是可定址,隨時準備處理中斷和系統呼叫。與此相反,使用者模式位址空間的對映隨程序切換的發生而不斷變化。

圖1 虛擬位址空間分布圖

使用者程序部分分段儲存內容:

名稱儲存內容

棧區域性變數、函式引數、返回位址等

堆動態分配的記憶體

bss段

未初始化或初值為0的全域性變數和靜態區域性變數

資料段已初始化且初值非0的全域性變數和靜態區域性變數

**段可執行**、字串字面值、唯讀變數

在將應用程序載入到記憶體空間執行時,作業系統負責**段、資料段和bss段的載入,並在記憶體中為這些段分配空間。棧也由作業系統分配和管理;堆由程式設計師自己管理,即顯式地申請和釋放空間。

bss段、資料段和**段是可執行程式編譯時的分段,執行時還需要棧和堆。

持續地重用棧空間有助於時活躍的棧記憶體保持在cpu快取中,從而加速訪問。程序中的每個執行緒都有屬於自己的棧。向棧中不斷壓入資料時,若超出其容量就會出現棧溢位狀況,從而觸發乙個頁錯誤。此時若棧的大小低於堆疊最大值rlimit_stack(通常是8m),則棧會動態增長,程式繼續執行。對映的棧區擴充套件到所需大小後,不再收縮。

堆疊的大小在執行時由核心動態調整。

共享庫

在編寫程式時,會依靠其他人已經寫好的許多**來執行例程或特殊功能。 這些**儲存在共享庫中使用它們,需要將它們與自己的**相鏈結,無論是在構建程式時還是在執行程式時。

堆(heap)

堆用於存放程序執行時動態分配的記憶體段,由程式設計師手動完成申請和釋放的。 堆中內容都是匿名的,不能通過名字直接訪問,只能通過指標間接訪問。如果用malloc申請空間,就用free釋放,如果用new申請,就用delete釋放。

分配的堆記憶體時經過位元組對齊的空間,以適合原子操作。堆管理器通過鍊錶管理每個申請的記憶體,由於堆申請和釋放是無序的,最終會產生記憶體碎片。堆記憶體一般由應用程式分配和釋放,**的記憶體可供重新使用。若程式設計師不釋放,程式結束時作業系統可能會自動**。

堆的末端由break指標標識,當堆管理器需要更多記憶體時,可通過系統呼叫brk()和sbrk()來移動break指標以擴張堆,一般由系統自動呼叫。

使用堆記憶體要注意兩個問題:

1)記憶體破壞。(釋放或改寫仍在使用的記憶體)

2)記憶體洩漏。(未釋放不再使用的記憶體)

【注意】此處堆不同於資料結構中的「堆」

bss

資料段:靜態記憶體分配,儲存未初始化的全域性及靜態變數(皆為0),可讀可寫。

【注意】儘管均放置於bss段,但初值為0的全域性變數是強符號,而未初始化的全域性變數是弱符號。若其他地方已定義同名的強符號(初值可能非0),則弱符號與之鏈結時不會引起重定義錯誤,但執行時的初值可能並非期望值(會被強符號覆蓋)。因此,定義全域性變數時,若只有本檔案使用,則盡量使用static關鍵字修飾;否則需要為全域性變數定義賦初值(哪怕0值),保證該變數為強符號,以便鏈結時發現變數名衝突,而不是被未知值覆蓋。

data資料段:靜態記憶體分配,儲存已初始化且不為0的全域性及靜態變數,可讀可寫。

資料段和bss段的區別:

1)bss段不占用物理檔案尺寸,但占用記憶體空間;資料段占用物理檔案,也占用記憶體空間。

對於大型陣列如int ar0[10000] = 和int ar1[10000],ar1放在bss段,只記錄共有10000*4個位元組需要初始化為0,而不是像ar0那樣記錄每個資料1、2、3…,此時bss為目標檔案所節省的磁碟空間相當可觀。

2)當程式讀取資料段的資料時,系統會觸發缺頁故障,從而分配相應的物理記憶體;當程式讀取bss段資料時,核心會將其轉到乙個全零頁面,不會發生缺頁故障,也不會為其分配相應的物理記憶體。

執行時資料段和bss段整個區段統稱為資料區。

text**段:儲存可執行機器碼(即程式執行**)和常量(如字串常量),可讀不可寫可執行,指令指標eip就是指向**段。通常**段是可共享的,因此頻繁執行的程式只需要在記憶體中擁有乙份拷貝即可。

**段指令根據程式設計流程依次執行,對於順序指令,只會執行一次(每個程序);若有反覆,則需要使用挑轉指令;若進行遞迴,則需要借助棧實現。

**段指令中包括操作碼和操作物件(或物件位址引用)。若操作物件是具體數值,將直接包含在**中;若是區域性資料,將在棧區分配空間,然後引用該資料位址;若位於bss和資料段,同樣引用該資料位址。

保留區

位於虛擬位址空間的最低部分,未賦予實體地址。任何對它的引用都是非法的,用於捕捉使用空指標和小整型值指標引用記憶體的異常情況。

它並不是乙個單一的記憶體區域,而是對位址空間中受到作業系統保護而禁止使用者程序訪問的位址區域的總稱。

記憶體分段的好處:

程序執行過程中,**指令根據流程依次執行,只需要訪問一次(跳轉和遞迴可能使**執行多次);資料(資料段和bss段)通常需要訪問多次,因此單獨開闢空間以便訪問和節約空間。

解釋:1)當程式被裝載後,資料和指令分別對映到兩個虛擬區域。資料區對於程序而言可讀寫,而指令區對於程序唯讀。兩區的許可權可分別設定為可讀寫和唯讀。以防止程式指令被有意或無意地改寫。

2)現代cpu具有極強的快取體系,程式必須盡量提高快取命中率。指令區和資料區的分離有利於提高程式的區域性性,故有利於提高cpu快取命中率。

3)當系統執行多個該程式的副本時,其指令相同,故記憶體中必須儲存乙份該程式的指令部分。若系統中執行數百個程序,通過共享指令將節省大量空間(尤其是對於動態鏈結的系統)。其他唯讀資料如程式中的圖示、、文字等資源也可共享。而每個副本程序的資料區域不同,它們是程序私有的。

4)臨時資料及需要再次使用的**在執行時放入棧區中,生命周期短。全域性資料和靜態資料可能在整個程式執行過程中都需要訪問,因此單獨儲存管理。堆區由使用者自由分配,以便管理

四、虛擬記憶體實現機制

圖2 虛擬記憶體實現

8. 位址對映

可執行檔案從磁碟對映到虛擬位址空間,

虛擬位址空間對映到實體地址空間

將可執行檔案從磁碟調入物理記憶體

把記憶體的內容換到磁碟,把磁碟內容換到記憶體,需要用到檔案系統

虛擬位址空間

當處理器讀或寫入記憶體位置時,它會使用虛擬位址。作為讀或寫操作的一部分,處理器將虛擬位址轉換為實體地址。通過虛擬位址訪問記憶體有以下優勢 程序可用的虛擬位址範圍稱為該程序的 虛擬位址空間 每個使用者模式程序都有其各自的專用虛擬位址空間。對於 32 位程序,虛擬位址空間通常為 2 gb,範圍從 0x0...

虛擬位址空間

14 共 14 對本文的評價是有幫助 評價此主題 程序可用的虛擬位址範圍稱為該程序的 虛擬位址空間 每個使用者模式程序都有其各自的專用虛擬位址空間。對於 32 位程序,虛擬位址空間通常為 2 gb,範圍從 0x00000000 至 0x7fffffff。對於 64 位程序,虛擬位址空間為 8 tb,...

虛擬位址空間

1 text 段 存放程式執行的一塊記憶體區域,此區域大小在執行之前就已經確定下來了。2 data資料段 全域性初始化資料區 靜態資料區 只初始化一次,指用來存放程式中已初始化的全域性變數的一塊記憶體區域 已經初始化的全域性變數 靜態變數和常量 3 bss未初始化資料區 用來存放程式中未初始化的全域...