Linux下C/C++動態庫在運行時是怎樣載入進來的?


這個動態庫在運行時是怎樣載入進實驗室那邊的程序里的?

在linux上,你在ps中說的那種"將動態庫作為一個參數傳到程序里"的使用方式,是通過dlopen函數將.so載入到當前進程中,並且通過ld.so將.so"鏈接"進當前進程。這個"鏈接"過程包括:查找未定義符號在當前進程中的地址、分配數據/代碼/bss段內存(數據初始化全局變數、代碼段重定位)、執行constructor函數等。之後,可以使用dlsym在已知符號名的情況下通過符號名查找符號對應的地址。這個符號可以是一個全局變數、全局函數等。在你說的C++中,重載的函數也可以理解為全局函數,會有一個屬性為weak的符號。該符號的符號名如果不做修改,默認按照System V的C++ API命名規範命名(以保證linux下不同編譯器編譯出來的.so和.o可以通用)。但如果使用extern "C"修飾之後,變成C的函數名,則無名稱修飾,便於使用。

它怎樣實例化我實現的繼承類?

實例化的方式和正常鏈接一樣。例如你在之類Derived中重載了基類Base中函數virtual void foo();那麼你需要在你的.so中導出一個可以new Derived()的函數,並且返回結果為Base *,這樣別人可以在沒有Derived定義的情況下獲得運行時類型為Derived的對象。此時別人雖然只有Base *的類型,但是仍然可以和正常鏈接一樣通過虛表查virtual void foo()的地址,從而調用你定義的foo()。

此外,如果你定義了繼承類的全局變數,在載入.so的時候該全局變數會自動初始化,你也可以將這個初始化的類通過指向Base *的指針傳出去。

如果有大神能解釋一下windows下動態庫和靜態庫的原理,小弟感激不盡!

和Linux差不多,不過在實現細節上有些出入。例如windows鏈接時要直接鏈接.dll需要通過鏈接生成這個.dll時生成的.lib,而Linux上直接鏈接.so即可。不過運行時鏈接無需這個.lib。靜態庫都是目標文件的壓縮包。都是ELF格式。至於共享內存之類的,僅僅取決於section的屬性。


我只能簡單回答下windows的情況。

靜態連接庫就是最早就出現的方式了,比如C runtime可以選擇用靜態庫的方式link。所謂靜態庫就是link的時候就直接把庫里所需要的內容和目標文件link到一起了,也就是說最後build出來的exe或者dll裡面包含了庫里編譯後的內容。顯然這種方式會使你的exe/dll的尺寸變大。

動態庫也就是DLL,它把庫的真正執行代碼放到一個單獨的文件裡面。好處有很多,首先你自己的可執行文件就不需要那麼大了;其次,如果這個DLL是系統自帶的,你都不需要在安裝包裡面提供這個文件;最後,在不同的進程中載入相同的DLL文件,其佔用的物理內存是通過mapped file共享的,這點對於節省windows整體內存開銷非常重要。

DLL其實還有兩種模式。

一種叫load time dynamic linking,就是說你的代碼裡面已經直接調用了庫裡面的函數,那麼在link的時候會把該庫的一小段lib link進去,這裡面包含了這個DLL的相關信息以便在真正運行時能找到那個dll。然後當你exe運行時,windows就會根據那些信息把需要用到的dll載入內存。

另一種叫run time dynamic linking。在編譯以及link的時候是並不需要提供這個庫的信息的。在程序真正運行的時候,通過自己調用LoadLibrary,GetProcAddress等API手工把DLL載入內存並找到裡面的函數來調用。


推薦閱讀:

為什麼 Linux 沒有註冊表?為什麼說註冊表是萬惡之源?
Linux Kernel 4.0 中的 live patching 是如何實現的?
如何用c++監控windows和linux文件夾中文件的變化,有沒有什麼api可以讓系統在保存文件的時候通知程序?
如何學會使用 Linux 操作系統?
Arch Linux 怎麼安裝?

TAG:操作系統 | 編程 | Linux | Ubuntu | C |