農林漁牧網

您現在的位置是:首頁 > 農業

程式如何執行?編譯、連結、裝入?

2022-03-15由 酷扯兒 發表于 農業

編譯程式是什麼

一、地址概念和程式如何執行

在多道程式環境下,要使程式執行,必須先為之建立程序。而建立程序的第一件事,便是

將程式和資料裝入記憶體

。如何將一個使用者源程式變為一個可在記憶體中執行的程式,通常都要經過以下幾個步驟:

首先是要編譯:

由編譯程式(Compiler)將使用者原始碼編譯成cpu可執行的目的碼,產生了若干個目標模組(Object Module)(即若干程式段)。形成的目的碼,

每個目的碼都是以0為基址順序進行編址

,原來用符號名訪問的單元用具體的資料——單元號取代。這樣生成的目標程式佔據一定的地址空間,稱為作業的邏輯地址空間,簡稱

邏輯空間

在邏輯空間中每條指令的地址和指令中要訪問的運算元地址統稱為邏輯地址 。很簡單,邏輯地址就是你源程式裡使用的地址,或者原始碼經過編譯以後編譯器將一些標號,變數轉換成的地址。

其次是連結

由連結程式(Linker)將編譯後形成的一組目標模組(程式段),以及它們所需要的庫函式連結在一起,形成一個完整的

裝入模組(Load Module)

最後是裝入(地址重定位)

由裝入程式(Loader)將裝入模組裝入物理記憶體。物理記憶體是真實存在的插在主機板記憶體槽上的記憶體條的容量的大小。

物理記憶體記憶體是由若干個儲存單元組成的,每個儲存單元有一個編號,這種編號可唯一標識一個儲存單元,稱為記憶體地址(或物理地址)。我們可以把記憶體看成一個從0位元組一直到記憶體最大容量逐位元組編號的儲存單元陣列,即每個儲存單元與記憶體地址的編號相對應。

裝入模組雖然具有統一的地址空間,但它仍是以“0”作為參考地址,即是浮動的。要把它裝入記憶體執行,就要確定裝入記憶體的實際物理地址,並修改程式中與 地址有關的程式碼,這一過程叫做地址重定位。

地址重定位主要是把邏輯地址轉換成物理記憶體絕對地址,這個工作又稱為地址對映。

圖 4-2 示出了這樣的三步過程。

程式如何執行?編譯、連結、裝入?

圖4-2 對使用者程式的處理步驟

二。 程式的連結

源程式經過編譯後,可得到一組目標模組,再利用連結程式將這組目標模組連結,形成裝入模組。根據連結時間的不同,可把連結分成如下三種:

(1) 、

靜態連結

。在程式執行之前,先將各目標模組及它們所需的庫函式,連結成一個完整的裝配模組,以後不再拆開。我們把這種事先進行連結的方式稱為靜態連結方式。

(2)、

裝入時動態連結

。這是指將使用者源程式編譯後所得到的一組目標模組,在裝入記憶體時,採用邊裝入邊連結的連結方式。

(3)、

執行時動態連結

。這是指對某些目標模組的連結,是在程式執行中需要該(目標)模組時,才對它進行的連結。

1.靜態連結方式(Static Linking)

我們透過一個例子來說明在實現靜態連結時應解決的一些問題。在圖 4-4(a)中示出了經過編譯後所得到的三個目標模組A、B、C,它們的長度分別為 L、M和N。在模組A中有一條語句CALL B,用於呼叫模組B。在模組B中有一條語句CALL C,用於呼叫模組C。B和C都屬於外部呼叫符號,在將這幾個目標模組裝配成一個裝入模組時,須解決以下兩個問題:

(1) 對相對地址進行修改。在由編譯程式所產生的所有目標模組中,使用的都是相對地址,其起始地址都為 0,每個模組中的地址都是相對於起始地址計算的。

在連結成一個裝入模組後,原模組B和 C在裝入模組的起始地址不再是 0,而分別是 L和 L+M,所以此時須修改模組B和C中的相對地址,即把原B中的所有相對地址都加上 L,把原 C中的所有相對地址都加上L+M。

(2) 變換外部呼叫符號。將每個模組中所用的外部呼叫符號也都變換為相對地址,如把B 的起始地址變換為 L,把 C 的起始地址變換為 L+M,如圖 4-4(b)所示。

這種先進行連結所形成的一個完整的裝入模組,又稱為可執行檔案。通常都不再拆開它,要執行時可直接將它裝入記憶體。這種事先進行連結,以後不再拆開的連結方式,稱為靜態連結方式。

程式如何執行?編譯、連結、裝入?

圖 4-4 程式連結示意圖

2.裝入時動態連結(Load-time Dynamic Linking)

使用者源程式經編譯後所得的目標模組,是在裝入記憶體時邊裝入邊連結的,即在裝入一個目標模組時,若發生一個外部模組呼叫事件,將引起裝入程式去找出相應的外部目標模組,並將它裝入記憶體,還要按照圖4-4所示的方式來修改目標模組中的相對地址。

裝入時動態連結方式有以下優點:

(1) 、

便於修改和更新

。對於經靜態連結裝配在一起的裝入模組,如果要修改或更新其中的某個目標模組,則要求重新開啟裝入模組。這不僅是低效的,而且有時是不可能的。若採用動態連結方式,由於各目標模組是分開存放的,所以要修改或更新各目標模組是件非常容易的事。

(2)、

便於實現對目標模組的共享

。在採用靜態連結方式時,每個應用模組都必須含有其目標模組的複製,無法實現對目標模組的共享。但採用裝入時動態連結方式,OS則很容易將一個目標模組連結到幾個應用模組上,實現多個應用程式對該模組的共享。

3.執行時動態連結(Run-time Dynamic Linking)

在許多情況下,應用程式在執行時,每次要執行的模組可能是不相同的。但由於事先無法知道本次要執行哪些模組,故只能是將所有可能要執行到的模組都全部裝入記憶體,並在裝入時全部連結在一起。顯然這是低效的,因為往往會有些目標模組根本就不執行。

比較典型的例子是作為錯誤處理用的目標模組,如果程式在整個執行過程中都不出現錯誤,則顯然就不會用到該模組。近幾年流行起來的執行時動態連結方式,是對上述在裝入時連結方式的一種改進。

這種連結方式是將對某些模組的連結推遲到程式執行時才進行連結,亦即,在執行過程中,當發現一個被呼叫模組尚未裝入記憶體時,立即由OS去找到該模組並將之裝入記憶體,把它連結到呼叫者模組上。凡在執行過程中未被用到的目標模組,都不會被調入記憶體和被連結到裝入模組上,這樣不僅可加快程式的裝入過程,而且可節省大量的記憶體空間。

三。 程式的裝入(地址的變換)

為了闡述上的方便,我們先介紹一個無需進行連結的單個目標模組的裝入過程。該目標模組也就是裝入模組。在將一個裝入模組裝入記憶體時,可以有

絕對裝入方式

可重定位裝入方式

動態執行時裝入方式

,下面分別簡述之。

1.絕對裝入方式(Absolute Loading Mode)

在編譯時,如果知道程式將駐留在記憶體的什麼位置,那麼,編譯程式將產生絕對地址的目的碼。即按照物理記憶體的位置賦予實際的物理地址。例如,事先已知使用者程式(程序)駐留在從R處開始的位置,則編譯程式所產生的目標模組(即裝入模組)便從R處開始向上擴充套件。

絕對裝入程式按照裝入模組中的地址,將程式和資料裝入記憶體。裝入模組被裝入記憶體後,由於程式中的邏輯地址與實際記憶體地址完全相同,故不須對程式和資料的地址進行修改。程式中所使用的絕對地址,既可在編譯或彙編時給出,也可由程式設計師直接賦予。

這個方式的優點:是

CPU執行目的碼快

缺點:

1)是由於記憶體大小限制,能裝入記憶體併發執行的程序數大大減少

2)編譯程式必須知道記憶體的當前空閒地址部分和其地址,並且把程序的不同程式段連續地存放起來,編譯非常複雜。由於程式

因此,通常是寧可在程式中採用符號地址,然後在編譯或彙編時,再將這些符號地址轉換為絕對地址。

如何把虛擬記憶體地址空間變換到記憶體唯一的一維物理線性空間?涉及到兩個問題:

一是虛擬空間的劃分問題。

二是把虛擬空間中已經連結和劃分好的內容裝入記憶體,並將虛擬空間地址對映記憶體地址的問題。即地址對映。地址對映就是建立虛擬地址與記憶體地址的關係。

2.靜態地址重定位(可重定位裝入方式 Relocation Loading Mode)

絕對裝入方式只能將目標模組裝入到記憶體中事先指定的位置。在多道程式環境下,編譯程式不可能預知所編譯的目標模組應放在記憶體的何處,因此,

絕對裝入方式只適用於單道程式環境。

在多道程式環境下,所得到的目標模組的起始地址通常是從 0 開始的,程式中的其它地址也都是相對於起始地址計算的。此時應採用可重定位裝入方式,根據記憶體的當前情況,將裝入模組裝入到記憶體的適當位置。

靜態地址重定位:即在程式裝入對目的碼裝入記憶體的過程中完成,是指在程式開始執行前,程式中指令和資料的各個地址均已完成重定位,即完成虛擬地址到記憶體地址對映。地址變換通常是在裝入時一次完成的,以後不再改變。

值得注意的是, 在採用可重定位裝入程式將裝入模組裝入記憶體後, 會使裝入模組中的所有邏輯地址與實際裝入記憶體的物理地址不同,圖4-3示出了這一情況。

程式如何執行?編譯、連結、裝入?

圖 4-3 作業裝入記憶體時的情況

例如,在使用者程式的 1000 號單元處有一條指令LOAD 1,2500,該指令的功能是將 2500 單元中的整數 365 取至暫存器 1。但若將該使用者程式裝入到記憶體的 10000~15000號單元而不進行地址變換, 則在執行11000號單元中的指令時,它將仍從 2500 號單元中把資料取至暫存器1而導致資料錯誤。

由圖4-3 可見,正確的方法應該是將取數指令中的地址 2500 修改成 12500,即把指令中的相對地址 2500 與本程式在記憶體中的起始地址 10000 相加,才得到正確的物理地址12500。除了資料地址應修改外,指令地址也須做同樣的修改,即將指令的相對地址 1000 與起始地址 10000 相加,得到絕對地址 11000。

優點:

無需硬體支援

缺點:

1)程式重定位之後就不能在記憶體中搬動了;

2)要求程式的儲存空間是連續的,不能把程式放在若干個不連續的區域中。

3.動態地址重定位(動態執行時裝入方式Dynamic Run-time Loading)

可重定位裝入方式可將裝入模組裝入到記憶體中任何允許的位置,故可用於多道程式環境;但這種方式並不允許程式執行時在記憶體中移動位置。因為,程式在記憶體中的移動,意味著它的物理位置發生了變化, 這時必須對程式和資料的地址(是絕對地址)進行修改後方能執行。

然而,實際情況是,在執行過程中它在記憶體中的位置可能經常要改變,此時就應採用動態執行時裝入的方式。

動態地址重定位:不是在程式執行之前而是在程式執行過程中進行地址變換。更確切的說,是把這種地址轉換推遲到程式真正要執行時才進行,即在每次訪問記憶體單元前才將要訪問的程式或資料地址變換成記憶體地址。

動態重定位可使裝配模組不加任何修改而裝入記憶體。為使地址轉換不影響指令的執行速度,這種方式需要一個重定位暫存器的支援,

優點:

1)

目標模組裝入記憶體時無需任何修改

,因而裝入之後再搬遷也不會影響其正確執行,這對於儲存器緊縮、解決碎片問題是極其有利的;

2)一個程式由若干個相對獨立的目標模組組成時,每個目標模組各裝入一個儲存區域,這些

儲存區域可以不是順序相鄰的,只要各個模組有自己對應的定位暫存器就行。

缺點:需要硬體支援。

四。 Windows NT動態連結庫

5。1。 構造動態連結庫

DLL是包含函式和資料的模組,它的呼叫模組可為EXE或DLL,它由呼叫模組在執行時載入;載入時,它被對映到呼叫程序的地址空間。在VC中有一類工程用於建立DLL。

庫程式檔案 。C:相當於給出一組函式定義的原始碼;

模組定義檔案 。DEF:相當於定義連結選項,也可在原始碼中定義;如:DLL中函式的引入和引出(dllimport和dllexport)。

編譯程式利用 。C檔案生成目標模組 。OBJ

庫管理程式利用 。DEF檔案生成DLL輸入庫 。LIB和輸出檔案 。EXP

連結程式利用 。OBJ和 。EXP檔案生成動態連結庫 。DLL。

5。2。 DLL的裝入方法

1)裝入時動態連結(load-time):

在程式設計時顯式呼叫某個DLL函式,該DLL函式在可執行檔案中稱為引入(import)函式。

連結時需利用 。LIB檔案。在可執行檔案中為引入的每個DLL建立一個IMAGE_IMPORT_DESCRIPTOR結構。

在裝入時由系統根據該DLL對映在程序中的地址改寫Import Address Table中的各項函式指標。Hint是DLL函式在DLL檔案中的序號,當DLL檔案修改後,就未必指向原先的DLL函式。在裝入時,系統會查詢相應DLL,並把它對映到程序地址空間,獲得DLL中各函式的入口地址,定位本程序中對這些函式的引用

裝入時動態連結過程:

(注:Import Address Table是在裝入時依據DLL模組的載入位置確定)。

程式如何執行?編譯、連結、裝入?

「DLL函式的呼叫過程:」

程式如何執行?編譯、連結、裝入?

2)執行時動態連結(run-time):

在程式設計時透過LoadLibrary(給出DLL名稱,返回裝入和連結之後該DLL的控制代碼), FreeLibrary, GetProcAddress(其引數包括函式的符號名稱,返回該函式的入口指標)等API來使用DLL函式。這時不再需要引入庫(import library)。

LoadLibrary或LoadLibraryEx把可執行模組對映到呼叫程序的地址空間,返回模組控制代碼;

GetProcAddress獲得DLL中特定函式的指標,返回函式指標;

FreeLibrary把DLL模組的引用計數減1;當引用計數為0時,拆除DLL模組到程序地址空間的對映;

執行時動態連結的例子:

HINSTANCE hInstLibrary;//模組控制代碼定義

DWORD (WINAPI *InstallStatusMIF)(char*, char*, char*, char*, char*, char*, char*, BOOL);//函式指標定義

if (hInstLibrary = LoadLibrary(“ismif32。dll”))//對映

{

InstallStatusMIF = (DWORD (WINAPI *)(char*,char*,char*, char*, char*, char*, char*, BOOL)) GetProcAddress(hInstLibrary, “InstallStatusMIF”);//獲得函式指標

if (InstallStatusMIF)

{

if (InstallStatusMIF(“office97”, “Microsoft”, “Office 97”, “999。999”, “ENU”, “1234”, ”Completed successfully”, TRUE) !=0)//呼叫DLL模組中的函式

{

}

}

FreeLibrary(hInstLibrary);//拆除對映

}

本文來源網路,版權歸原作者所有。如涉及作品版權問題,請聯絡我進行刪除。