http://zh.wikipedia.org/zh-tw/%E5%8A%A8%E6%80%81%E9%93%BE%E6%8E%A5%E5%BA%93
動態鏈結媒體櫃(Dynamic Link Library或者Dynamic-link library,縮寫為DLL),又稱為動態連結函式庫,是微軟公司在微軟視窗作業系統中實作共享函式庫概念的一種實作方式。這些媒體櫃函式的副檔名是.DLL、.OCX(包含ActiveX控制的媒體櫃)或者.DRV(舊式的系統驅動程式)。
所謂動態鏈結,就是把一些經常會共用的程式碼(靜態鏈結的OBJ程式庫)製作成DLL檔,當執行檔呼叫到DLL檔內的函數時,windows作業系統才會把DLL檔載入記憶體內,DLL檔本身的結構就是可執行檔,當程式需求函數才進行鏈結。透過動態鏈結方式,記憶體浪費的情形將可大幅降低。
DLL的文件格式與視窗EXE文件一樣——也就是說,等同於32位視窗的可移植執行文件(PE)和16位視窗的New Executable(NE)。作為EXE格式,DLL可以包括程式碼、資料和資源的多種組合。
在更廣泛的意義上說,任何同樣文件格式的資料文件都可以稱作資源DLL。這樣的DLL的例子有有副檔名ICL為的圖示媒體櫃、副檔名為FON和FOT的字型文件。
DLL的最初目的是節約應用程式所需的磁碟和記憶體空間。在一個傳統的非共享媒體櫃中,一部份程式碼簡單地附加到呼叫的程式上。如果兩個程式呼叫同
一個子程式,就會出現兩份那段程式碼。相反,許多應用共享的程式碼能夠切分到一個DLL中,在硬碟上存為一個文件,在記憶體中使用一個例項
(instance)。DLL的廣泛應用使得早期的視窗能夠執行在緊巴巴的記憶體條件下。
DLL提供了如模組化這樣的共享媒體櫃的普通好處。模組化允許僅僅更改幾個應用程式共享使用的一個DLL中的程式碼和資料而不需要更改應用程式自身。這種模組化的基本形式允許如Microsoft Office、Microsoft Visual Studio、甚至微軟視窗自身這樣大的應用程式使用較為緊湊的補丁和服務包。
模組化的另外一個好處是外掛程式的通用介面使用。單個的介面允許舊的模組與新的模組一樣能夠與以前的應用程式執行時無縫地整合到一起,而不需要對應用程式本身作任何更改。這種動態擴充套件的思想在ActiveX中發揮到了極致。
儘管有這麼多的優點,使用DLL也有一個缺點:DLL地獄,也就是幾個應用程式在使用同一個共享DLL媒體櫃發生版本衝突。這樣的衝突可以透過將不同版本的問題DLL放到應用程式所在的資料夾而不是放到系統資料夾來解決;但是,這樣將抵消共享DLL節約的空間。目前,Microsoft .NET將解決DLL hell問題當作自己的標的,它允許同一個共享媒體櫃的不同版本並列共存。由於現代的電腦有足夠的磁碟空間和記憶體,這也可以作為一個合理的實作方法。
記憶體管理
在Win32中,DLL文件按照片段(sections)進行組織。每個片段有它自己的內容,如可寫或是唯讀、可執行(程式碼)或者不可執行(資料)等等。
DLL程式碼段通常被使用這個DLL的處理程序所共享;也就是說它們在實體記憶體中佔據一個地方,並且不會出現在頁面文件中。如果程式碼段所佔據的實體記憶體被收回,它的內容就會被放棄,後面如果需要的話就直接從DLL文件重新載入。
與程式碼段不同,DLL的資料段通常是私有的;也就是說,每個使用DLL的處理程序都有自己的DLL資料副本。作為選擇,資料段可以設定為共享,允許透過這個共享記憶體區域進行處理程序間通訊。但是,因為使用者許可權不能應用到這個共享DLL記憶體,這將產生一個安全漏洞;也就是一個處理程序能夠破壞共享資料,這將導致其它的共享處理程序異常。例如,一個使用訪客賬號的處理程序將可能透過這種方式破壞其它執行在特權賬號的處理程序。這是在DLL中避免使用共享片段的一個重要原因。
當DLL被如UPX這樣一個可執行的packer壓縮時,它的所有程式碼段都標記為可以讀寫並且是非共享的。可以讀寫的程式碼段,類似於私有資料段,是每個處理程序私有的並且被頁面文件備份。這樣,壓縮DLL將同時增加記憶體和磁碟空間消耗,所以共享DLL應當避免使用壓縮DLL。
符號解析和繫結
DLL輸出的每個函式都由一個數位序號唯一標識,也可以由可選的名字標識。同樣,DLL引入的函式也可以由序號或者名字標識。對於內部函式來說,只
輸出序號的情形很常見。對於大多數視窗API函式來說名字是不同視窗版本之間保留不變的;序號有可能會發生變化。這樣,我們不能根據序號引用視窗API函
式。
按照序號引用函式並不一定比按照名字引用函式效能更好:DLL輸出表是按照名字排列的,所以對半尋找可以用來在在這個表中根據名字尋找這個函式。另外一方面,只有線性尋找才可以用於根據序號尋找函式。
將一個可執行文件繫結到一個特定版本的DLL也是可能的,這也就是說,可以在編譯時解析輸入函式(imported
functions)的網址。對於繫結的輸入函式,連結工具保存了輸入函式繫結的DLL的時間戳和校驗和。在執行時Windows檢查是否正在使用同樣版
本的媒體櫃,如果是的話,Windows將繞過處理輸入函式;否則如果媒體櫃與繫結的媒體櫃不同,Windows將按照正常的方式處理輸入函式。
繫結的可執行文件如果執行在與它們編譯所用的環境一樣,函式呼叫將會較快,如果是在一個不同的環境它們就等同於正常的呼叫,所以繫結輸入函式沒有任
何的缺點。例如,所有的標準Windows應用程式都繫結到它們各自的Windows發布版本的系統DLL。將一個應用程式輸入函式繫結到它的目的環境的
好機會是在應用程式安裝的過程。
執行時顯式鏈結
對每個DLL來說,Windows儲存了一個全域計數器,每多一個行程使用便多額外一個。LoadLibrary與FreeLibrary指令影響
每一個行程內含的計數器;動態連結則不影響。因此藉由呼叫FreeLibrary多次,從記憶體反載入一DLL是很重要的。一個行程可以從它自己的VAS
註銷此計數器。
DLL文件能夠在執行時使用LoadLibrary(或者LoadLibraryEx)API函式進行顯式呼叫,這個的過程微軟簡單地稱為執行時動態呼叫。API函式GetProcAddress根據尋找輸出名稱符號、FreeLibrary移除DLL。這些函式類似於POSIX標準API中的dlopen、dlsym、和dlclose。
注意微軟簡單稱為執行時動態鏈結的執行時隱式鏈結,如果不能找到鏈結的DLL文件,Windows將提示一個錯誤訊息並且呼叫應用程式失敗。應用程式開發人員不能透過編譯鏈結來處理這種缺少DLL文件的隱式鏈結問題。另外一方面,對於顯式鏈結,開發人員有機會提供一個完善的出錯處理機制。
執行時顯式鏈結的過程在所有語言中都是相同的,因為它依賴於Windows API而不是語言結構。