1. 程式人生 > 實用技巧 >《程式設計師的自我修養》p6 可執行檔案的裝載與程序

《程式設計師的自我修養》p6 可執行檔案的裝載與程序

程序虛擬地址空間

每個程式被執行起來以後,都將擁有自己獨立的虛擬地址空間,這個虛擬地址空間的大小由計算機的硬體平臺決定,即由CPU的位數決定。
一般來說,C語言指標大小的位數與虛擬空間的位數相同
我們下文以32位的地址空間為主進行討論:
在這裡插入圖片描述

整個4GB被劃分為兩部分,其中作業系統本身用去了一部分:從地址0xC0000000到地址0xFFFFFFFF,共1GB;剩下的0x00000000到0xBFFFFFFF共3GB都是留給程序使用的;也就是說整個程序在執行的時候,所有的程式碼、資料包括通過C語言malloc()等方法申請的虛擬空間之和不可以超過3GB。

裝載的方式

覆蓋裝入和頁對映是兩種很典型的動態裝載方法,都是利用了程式區域性性原理。

動態裝入的思想就是用到哪個模組,就將哪個模組裝入記憶體,如果不用就暫時不裝入,存放在磁碟中。

1、覆蓋裝入

由覆蓋管理器來管理模組程式碼何時應該駐留在記憶體而何時應該被替換掉;
當存在多個模組時,程式設計師需要手工將模組按照他們之間的依賴關係組織成樹狀結構
在這裡插入圖片描述

有兩點需要保證:
1) 這個樹狀結構中從任何一個模組到樹根(也就是main)模組叫做呼叫路徑。當該模組被呼叫時,整個呼叫路徑上的模組都必須在記憶體中。
2) 禁止跨樹間呼叫

2、頁對映

與覆蓋裝入原理相似,頁對映也不是一下子就把程式的所有資料和指令都裝入記憶體,而是將記憶體和所有磁碟中的資料和指令按照“頁”為單位劃分成若干個頁,以後所有的裝載和操作的單位都是頁。

這就是現代作業系統中的儲存管理器,幾乎所有主流作業系統都是按照這種方式裝載可執行檔案的。

從作業系統角度看可執行檔案的裝載

程序的建立

從OS角度來看,一個程序最關鍵的特徵是它擁有獨立的虛擬地址空間,這使得它有別於其他程序。很多時候一個程式被執行同時都伴隨著一個新的程序被建立,那麼我們來看一下這種最通常的情形:建立一個程序,然後裝載相應的可執行檔案並且執行。在有虛擬儲存的情況下,上述過程最開始只需要做三件事情:
1) 建立一個獨立的虛擬地址空間;
2) 讀取可執行檔案頭,並且建立虛擬檔案空間與可執行檔案的對映關係;
3) 將CPU的指令暫存器設定成可執行檔案的入口地址,啟動執行。

首先是建立虛擬地址空間

.我們知道虛擬空間由一組頁對映函式將虛擬空間的各個頁對映至相應的物理空間,那麼建立一個虛擬空間實際上並不是建立空間而是建立對映函式所需要的相應的資料結構;
讀取可執行檔案頭,並且建立虛擬空間與可執行檔案的對映關係。頁對映關係函式是虛擬空間到實體記憶體的對映關係,這一步做的是虛擬空間和可執行檔案的對映關係。(當作業系統捕捉到缺頁錯誤時,應該知道程式當前所需要的頁在可執行檔案的哪一個位置,這就是虛擬空間與可執行檔案之間的對映關係)

由於可執行檔案在裝載時實際上是被對映的虛擬空間,所以可執行檔案很多時候又被叫做映像檔案
在這裡插入圖片描述

很明顯,這種對映關係只是儲存在作業系統內部的一個數據結構。Linux空間中的一個段叫做虛擬記憶體區域(VMA),在Windows中將這個叫做虛擬段。
在上例中,當作業系統建立程序後,會在程序相應的資料結構中設定一個.text段的VMA:它在虛擬空間中的地址為0x08048000~0x08049000,它對應ELF檔案中偏移為0的.text;

將CPU指令暫存器設定成可執行檔案入口,啟動執行。在程序角度看就是執行了一條跳轉指令,直接跳轉到可執行檔案的入口地址;

頁錯誤

在這裡插入圖片描述

程序虛存空間分佈

ELF檔案連結檢視和執行檢視

ELF被對映時,是以系統的頁長度作為單位的,那麼每個段在對映時的長度應該都是系統頁長度的整數倍;如果不是,那麼多餘的部分也將佔用一個頁,這會造成許多記憶體浪費。

作業系統裝載可執行檔案時,它不關心可執行檔案各個段所包含的實際內容,只關心一些跟裝載相關的問題,最主要的是段的許可權:
1) 以程式碼段為代表的許可權是可讀可執行的段;
2) 以資料段和BSS段為代表的許可權是可讀可寫的段;
3) 以只讀資料段為代表的許可權為只讀的段

我們選擇的方案是:對於相同許可權的段,把它們合併到一起當做一個段進行對映
ELF可執行檔案引入了一個概念叫做“segment”,一個“segment”包含一個或多個屬性類似的“sectioon”,這樣對映以後在程序虛存空間中就只有一個相對應的VMA,可以很明顯減少頁面內部碎片,從而節省了記憶體空間。

“Segment”實際上是從裝載的角度重新劃分了ELF各個段,把那些屬性相似的、又連在一起的段叫做一個“Segemnt”,而系統正是按照“Segment”而不是“section”來對映可執行檔案的。
描述“section”屬性的結構叫做段表,描述“segment”的結構叫做程式頭,它描述了ELF檔案該如何被作業系統對映到程序的虛擬空間

堆和棧

在作業系統中,VMA除了被用來對映可執行檔案的各個“Segment”以外,作業系統也可以通過使用VMA來對程序的地址空間進行管理。程序在執行時候的堆、棧等空間,它們在程序的虛擬地址中的表現也是以VMA的形式存在的,很多情況下,一個程序中的棧和堆分別都有一個對應的VMA。

堆的最大申請數量

段地址對齊

程序棧初始化

Linux核心裝載ELF過程簡介

在這裡插入圖片描述