?? 最初發(fā)表在 這里 。
?
?? 其實,C/C++世界開始時并沒有庫這個概念,我們編寫程序的時候,都是自己搞定一切:Coding,Compile,Link,生成一個可執(zhí)行文件后載入系統(tǒng)運行就可以了。但是,如果每個程序員都這樣各自為政的話,將會導(dǎo)致大量的重復(fù)勞動。譬如,在很多程序中都需要輸入輸出的功能,按照現(xiàn)在這種狀況,只有每個程序員都自己重新開發(fā)這樣的功能模塊,這樣效率之低下可想而知。于是,大家渴望能夠進行代碼重用:一些通用的代碼最好能夠由別人提供,我只需調(diào)用即可。
?? 那么,我們?nèi)绾蔚玫竭@些可重用的代碼呢?首先我們想到可以讓編譯器自動為我們生成這些代碼。我們只需調(diào)用這些函數(shù),編譯器解析到這些函數(shù)時,如C語言的 printf函數(shù),則自動為我們生成相應(yīng)的代碼。嗯,確實是個可行的辦法。Pascal中的一些標準方法就是這樣提供的。但是,這個方法也有很大的缺陷:第一,就是大大加重了編譯器的負擔,使編譯器復(fù)雜化,對于C語言這樣的標準方法很多的語言更是如此。第二,很難對這些方法進行添加或者更新,如果有這樣的需要,則只能重新編寫和編譯整個編譯器。
?? 第二個方法就是將所有的標準函數(shù)都編譯到一個可重定位模塊中去,譬如,我們可以將printf,scanf等標準函數(shù)統(tǒng)一編譯到一個libc.o文件中,然后,我們就可以把它連接到我們的程序中去。這個方法對于程序員來說相當方便,因為我們只需要指定一個連接模塊就可以使用各種函數(shù)了。但是,對于計算機來說,這并不是一件好事:它太龐大了!如果我的程序中僅僅需要其中一個printf 函數(shù),我也必須得把整個libc.o連接進去,而這個libc.o的大小通常都是以M為單位的,成本太高了,不可行。
??? 那好,如果這樣空間成本太高的話,我們可以將其化整為零,每個方法編譯成一個.o文件,譬如printf.o,scanf.o等等。對,這樣一來,計算機是滿意了,但是我們程序員卻慘了:你想想,如果我在應(yīng)用程序中使用了30個函數(shù),那么我在連接的時候,需要準確無誤地提供這三十個函數(shù)對應(yīng)的.o文件,痛苦啊!
?? 有沒有一個兩全其美的方法,既能夠減少對內(nèi)存的占用,又方便程序員使用呢?有,靜態(tài)庫(static library)就是這樣一個解決方案。靜態(tài)庫是一個或多個.o文件的集合。程序員使用到這些.o文件對應(yīng)的函數(shù)時,只需要在連接時提供該靜態(tài)庫即可,而不需要列舉用到的.o文件。而連接器進行連接的時候,只會將程序中用到的函數(shù)對應(yīng)的.o文件連接到程序中。譬如,我們有一個庫文件libc.a,里面放有printf.o,scanf.o等多個.o文件,如果我們程序中只使用了printf函數(shù),那么,連接器只會將printf.o連接進來,而不會把scanf.o也連接進來,雖然它們都同在一個庫文件中。這樣,計算機和程序員都滿意了。下圖( from Apple )是使用靜態(tài)庫的示例:
??? 那是不是有了靜態(tài)庫以后就萬事大吉了呢?當然不是,靜態(tài)庫是在編譯階段跟應(yīng)用代碼連接在一起的,從那以后,兩者就緊密耦合在一起。這樣一來,應(yīng)用代碼中庫文件的更新就成了大問題了。如果庫文件更新了,我想在應(yīng)用程序中使用到最新的靜態(tài)庫,那么,我只能夠?qū)⑽业拇a跟新的靜態(tài)庫重新編譯一次,很難維護。此外,靜態(tài)庫的性質(zhì)使每一個應(yīng)有程序都有靜態(tài)庫中相應(yīng)部分的拷貝,譬如,程序A和B都用到了printf函數(shù),那么在兩個程序中都會有printf.o拷貝。如果系統(tǒng)中有好幾十個進程都使用到了printf函數(shù),那么相同的代碼將會重復(fù)出現(xiàn)幾十次,這對于內(nèi)存是極大的浪費。針對這些問題,共享庫(shared library,也叫做動態(tài)連接庫,*nix中為so文件,Windows中稱為Dll)誕生了。使用該技術(shù),一個共享庫只會在系統(tǒng)中出現(xiàn)一次,而不管系統(tǒng)中有多少個進程使用到這個共享庫。同時,共享庫中的代碼段還可以被各個進程所共享。共享庫的示例圖(from Apple)如下:
???? 共享庫是在應(yīng)用代碼裝載到系統(tǒng)中的時候由動態(tài)連接器動態(tài)加載到內(nèi)存空間去的。靜態(tài)連接器在編譯階段只是在應(yīng)用代碼中插入一些關(guān)于共享庫的基本信息,而不會將共享庫的實際代碼連接到應(yīng)用代碼中去。這樣,如果庫文件更新了,我們只需要重新啟動應(yīng)用程序,就可以使用到最新的庫文件了,同時,我們還大大節(jié)省了內(nèi)存,一舉兩得。
???? 現(xiàn)在,一切都那么美好,但是并非完美。現(xiàn)在的情況是在加載過程中我們會把應(yīng)用代碼中的使用到共享庫動態(tài)加載到系統(tǒng)中,但是,這個庫在實際運行過程中會被使用到嗎?看下面例子:
{
???? ?if( cmd == 0 )
?? ?{
?????? ?//use library method
???? }
}
??? 如果傳入的參數(shù)不等于 0 的話,libraay method是永遠都不會執(zhí)行的,但是,我們卻會把這個共享庫加載到系統(tǒng)中去。如果我們能夠在運行過程中決定是否加載一個共享庫,那該多好啊!完全可以,Linux以及相關(guān)系統(tǒng)我們提供了相關(guān)的API,這些API包含在頭文件<dlfcn.h>中。相關(guān)的函數(shù)有:
void *dlsym(void *handle, char *symbol);
int dlclose (void *handle);
const char *dlerror(void);
??? 在VC,也有相應(yīng)的函數(shù)完成相關(guān)功能。此外,VC中還有延遲加載技術(shù),使得Dll可以按需加載。
補充一段共享庫存在的問題:from [4]
DLL Hell
While it is usually a good idea to share a single instance of a library rather than reduplicate it into every individual program, this sharing can become a serious problem. Think of two different Web browsers that reside on the same machine. Both browsers use the same shared libraries to access the system's modem, graphics card, and I/O routines. Now, suppose that you recently had to install an update of one of these shared libraries because of a security loophole found in the original version. Alas, the new version causes one of the browsers to crash. You can either wait for a new patch or revert to the previous version of the shared library.
None of these workarounds is ideal. Had the library been statically linked into each browser application, this mess would have been avoided. Unfortunately, such conflicts are reconciled only when you explicitly enable multiple versions of the same shared library to coexist on the same machine. However, this workaround complicates system management and forces users to become experts. In the Windows world, this problem is called the "DLL hell" although it exists in the POSIX world too.
Aliasing
?
Dynamic linking enables you to share identical code, which is usually a good idea. However, sharing the same data is rarely desirable.
If, for example, two processes call localtime() , it must return a different local static tm struct for every process. Designers of dynamically linked libraries were aware of this necessity and devised various techniques to cope with it. However, programmers must explicitly state which components of a shared library are truly shared and which ones aren't. The result is a larger and slower shared library as well as cluttered-up source files.
Code Cracking
Windows programmers who wish to thwart crackers' attempt to decompile their code often split an application into multiple DLLs. Ironically, this makes a cracker's job much easier because DLLs (and shared libraries in general) must contain
metacode
, i.e., code that documents code. This metacode (reminiscent of debug information) tells the runtime linker where each function and object is stored in the file, what their
non-decorated names
are, and so on. A hacker that knows how this information is encoded can decompile the code with less effort than required for decompiling an optimized, statically linked executable
Reference:
[1] Computer Systems: A Programmer's Perspective,Chapter 7 Linking
[2]
Program Library HOWTO
[3]
Overview of Dynamic Libraries
?, Apple
[4]
Dynamic Linking: Advantages and Disadvantages
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
