WIN下和LINUX動態庫的區別 - 轉
轉載:https://blog.codingnow.com/2006/11/windows_unix_dynamic_library.html
最近慢慢將開發環境轉向了 freebsd ,漸漸的發現了許多東西跟 Windows 下不太一樣。我指的是,跟我以前想當然的理解不太一樣。比如,unix 下對動態連結庫 so 的處理,和 Windows 下的 DLL 就不太相同。
之前,我對 Windows 下更為了解一些。早幾年做 Windows 開發,老是為動態連結,靜態連結這些問題糾纏不清;慢慢的才有了比較清晰的理解,現在基本上不會為這類問題困繞了。前段時間,碰到一些朋友寫 lua 的擴充套件庫(windows 版本)時遇到的幾個 bug ,就是因為連結問題導致的。公司內部的一個專案,在伺服器程式上也碰到了錯誤連結 lua 引起的 bug ,在內部 maillist 上爭論了好久。當時,作為 windows 程式設計師,跟同事中的 unix 程式設計師爭論看似相同的問題時,卻老說不到一起去;今天才發現,原來是相互之間對動態連結庫的理解不同導致的。
今天,寫下本文,或許可以讓以後跨平臺開發的朋友少走點彎路。
動態連結庫在 unix 下,習慣以 .so 為檔名結尾(通常還以 lib 開頭)。而 Windows 下是以 .DLL 為檔案字尾。Windows 在處理 dll 上還有一些細節容易被人忽略,我曾經為這個寫過一篇 Blog。
如果需要執行時主動載入一個動態連結庫,windows 下可以使用 LoadLibrary 這個 kernel API (在 kernel32.dll 中);unix 下是用 dlopen 。Windows 下找到 dll 中匯出符號的地址,可以用 GetProcAddress ,而 unix 也有對應的 api ...
這些相互對應的 api ,似乎預示著對等的功能,但事實上是有區別的。
DLL 事實上和 EXE 檔案一樣,同屬 PE 格式的執行檔案。對於隱式的引用外部符號,需要把外部符號所在的位置寫在 PE 頭上。PE 載入器將從 PE 頭上找到依賴的符號表,並載入依賴的其它 DLL 檔案。
但是,unix 上並非如此,so 檔案大多為 elf 執行檔案格式。當它們需要的外部符號,可以不寫明這些符號所在的位置。也就是說,通常 so 檔案本身並不知道它依賴的那些符號在哪些 so 裡面。這些符號是由呼叫 dlopen 的程序執行時提供的。而 unix 下的執行檔案本身會暴露自己靜態連結的符號,(可以是自己本身實現的,或者是從靜態庫 .a 檔案裡鏈入的)。dlopen 將把這些符號通報給 dlopen 載入的 .so 檔案,最終完成動態連結。(事實上 dlopen 還可以指定 mode ,完成更復雜的操作)
因為這個區別,unix 下的 lua 直譯器可以完全靜態連結所有的 lua api ;我們為 lua 擴充套件的庫,以 so 的形式存在被執行時載入不會有任何隱患。而 Windows 下,必須生成一個 luacore 的 DLL 檔案,由 lua 直譯器於擴充套件庫共享 lua api (還包括 crt 的實現) 才不會出問題。
也因為這個區別,VC 下才會有讓 windows 開發新手困惑不解的動態連結 CRT ,靜態連結 CRT ,多執行緒庫,單執行緒庫,等等的選項。沒點點 windows 開發功力的人,多少都要在上面栽幾個跟頭。
從動態連結庫的這個設計上來看,我個人感覺,Windows 弄的真是糟糕透頂。尤其是對開發者來說是這樣。至少我們在 windows 下做一個 dll 檔案給大家使用還需要攜帶一個 .lib 檔案;而 unix 下一般只需要有相應的標頭檔案就夠了。對於編寫新的 .so ,找不到的符號可以就讓它在那裡,直到最終執行檔案來把所有需要的符號聯合到一起。windows 可以存在一個 dll 對另一個 dll 的隱式依賴;而 unix 下一般不需要讓 so 和 so 有隱式依賴關係。這讓我們全域性替換類似 CRT 的東西變的困難許多;而以我自己的程式設計經驗來看,好處卻沒有多少。
順便一提的是 unix 下需要用 ldconfig 來管理動態庫,這比 windows 下 copy DLL 到當前目錄下就可以用的方式,無疑提高了系統的安全性。