如果這個問題涉及的問題太基本,請見諒。
作為一個有點接近初學者的程式員,我真的很想知道這個問題——每個win API函式的底層代碼是否在撰寫應用程式時完全編譯,或者執行win API的機器代碼是否保留自PC啟動以來,作為作業系統的一部分在記憶體中,并且只有應用程式使用它們?
一個作業系統的所有 API 都被許多應用程式通過函式呼叫使用。所以我認為與其讓每個單獨的應用程式都包含自己的 API 機器代碼,不如應用程式只包含呼叫 API 的標頭或簽名,并且在啟動應用程式時映射 API 機器代碼地址。
很抱歉,由于我的英語不好,我未能使這個問題簡潔。我真的很想得到你的見解。謝謝你。
uj5u.com熱心網友回復:
(大多數)API 呼叫的實作是由系統通過編譯模塊(可移植可執行影像)提供的。應用程式代碼只包含足夠的資訊,以便系統可以識別和加載所需的模塊,并決議相應的匯入。
例如,考慮以下代碼,它顯示一個訊息框,等待它關閉,然后退出程式:
#include <Windows.h>
int main()
{
::MessageBoxW(nullptr, L"Foo", L"Bar", MB_OK);
}
給定函式簽名(在WinUser.h中宣告,從Windows.h中獲取),編譯器幾乎可以生成一條call
指令。它知道引數的數量,它們的預期型別,以及被呼叫者期望它們的順序和位置。缺少的是user32.dll中的實際目標地址,只有在行程完全初始化后才知道,并且有user32.dll模塊映射到其地址空間。
顯然,編譯器不能將代碼生成推遲到加載時間之后。它現在需要生成一條call
指令。因為我們知道“計算機科學中的所有問題都可以通過另一個間接級別來解決”,這也是編譯器所做的:它不會發出直接呼叫指令,而是生成間接呼叫。不同之處在于,直接呼叫需要立即提供目標地址,而間接呼叫可以指定存盤目標地址的地址。
在 x86 匯編中,不必說
call _MessageBoxW@16 ; uh-oh, not yet known
編譯器可以方便地將呼叫委托給匯入地址表(IAT):
call dword ptr [__imp__MessageBoxW@16]
避免了災難,我們已經為我們贏得了足夠的時間在代碼實際執行之前修復問題。
一旦創建了行程物件,系統就會將控制權交給其主執行緒以完成初始化。該初始化的一部分是加載依賴項(例如此處的user32.dll)。一旦完成,系統最終知道加載地址(以及最終匯入符號的地址,例如),并且可以用匯入的函式地址_MessageBoxW@16
覆寫地址處的 IAT 條目。__imp__MessageBoxW@16
這大致就是系統如何為系統服務提供實作,而不需要客戶端應用程式知道(物理上)他們會在哪里找到它們。
我說“大約”是因為事情更多地涉及到現實中。如果這是您想了解的內容,我會留給 Raymond Chen。他發表了一系列更詳細地涵蓋該主題的博客文章:
- DLL 函式是如何在 16 位 Windows 中匯出的?
- DLL 函式是如何在 16 位 Windows 中匯入的?
- 在 32 位 Windows 中如何匯出 DLL 函式?
- 真正是轉發器的匯出函式
- 重新思考 32 位 Windows 決議 DLL 匯出的方式
- 呼叫一個匯入的函式,天真的方式
- 不那么天真的編譯器如何呼叫匯入的函式
- 與強制為匯入的函式創建存根相關的問題
- 當你得到 dllimport 錯誤時會發生什么?
- 匯入庫中的名稱被修飾是有原因的
- 為什么我不能 GetProcAddress 一個我 dllexport'ed 的函式?
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/470847.html