標籤:
原文網址:http://blog.csdn.net/heyabo/article/details/11688517
申明: 正如題如示,本篇講的是Linux下是靜態庫與共用庫,而Window下的動態連結程式庫詳細情況可見這篇文章:windows動態連結程式庫 DLL 淺析。雖然原理,思想差不多,但是細節卻各有不同。
一、靜態庫
1、概念:靜態庫指將所有相關的目標檔案打包成為一個單獨的檔案-即靜態庫檔案,以.a結尾。靜態庫可作為
連結器的輸入,連結器會將程式中使用的到函數的代碼從庫檔案中拷貝到應用程式中。
一旦連結完成,在執行程式的時候就不需要靜態庫了。
注1:由於每個使用靜態庫的應用程式都需要拷貝所用函數的代碼,所以靜態連結的檔案會比較大。
注2:在Unix系統中,靜態庫以一種稱為存檔(archive)的特殊檔案格式存放在磁碟中。
封存檔案是一組串連起來的可重定位目標檔案的集合,有一個頭部用來描述每個成員目標檔案的大小和位置(封存檔案名由尾碼.a標識)。
2、建立與應用 假設我們想在一個叫做libvector.a的靜態庫中提供以下向量函數:
[cpp] view plaincopyprint?
- // addvec.c
- void addvec(int* x, int* y, int*z, int n)
- {
- int i=0;
- for(; i< n;++i)
- z[i] = x[i] + y[i];
- }
[cpp] view plaincopyprint?
- // multvec.c
- void multvec(int*x, int* y, int* z, int n)
- {
- int i = 0;
- for(; i < n; ++i)
- z[i] = x[i] * y[i];
- }
使用
AR工具建立靜態庫檔案: 為了
使用這個庫,編寫一個應用(其調用addvec庫中的函數):
[cpp] view plaincopyprint?
- /* main2.c */
- #include <stdio.h>
-
- int x[2] = {1, 2};
- int y[2] = {3, 4};
- int z[2]={0};
-
- int main()
- {
- addvec(x, y, z, 2);
- printf("z = [%d %d]\n", z[0], z[1]);
- return 0;
- }
編譯-
連結-
運行程式:
注1:
-static 參數告訴編譯器驅動程式,連結器應該構建一個完全的可執行目標檔案,它可以載入到儲存空間並運行,在載入時無需進一步的連結 -
即一次性靜態連結完畢,不允許存在動態連結。
注2:當連結器運行時,它判定addvec.o定義的addvec符號是被main2.o引用的,所以它拷貝addvec.o到可執行檔。因為程式中沒有引用任何由multvec.o定義的符號,所以連結器就不會拷貝這個模組到可執行檔。同時,連結器還會拷貝libc.a中的pirintf.o模組,以及許多C運行時系統中的其他模組。連結器完整的行為可如所示:
二、共用庫
1、概念:共用庫是一個目標模組(以.so尾碼表示),在運行時,可以載入到任意的儲存空間地址,並和一個在儲存空間中的程式連結起來,這個過程稱為動態連結,是由一個叫做
動態連結器的程式來執行的。
2、分類:根據載入和連結共用庫的
時機又可分為:
A)應用程式
自身載入時動態連結和載入共用庫;
B)應用程式
運行過程中動態連結和載入共用庫兩種情況。
2-A:應用程式自身載入時動態連結和載入共用庫
2-A.1 基本思路是:當建立可執行檔時,靜態執行一些連結(共用庫的重定位和符號表資訊,而非代碼和資料),然後在應用程式載入時,動態完成連結過程。
2-A.2 建立與應用 建立類似於靜態庫的建立,假設我們現在想在一個叫做libvector.so的共用庫庫中提供以下addvec和multvec函數:下面使用
-shared選項來指示連結器建立一個共用的目標檔案(即共用庫),連結並運行程式:
注1:
-fPIC選項指示編譯器產生與位置無關的代碼其動態連結過程可如所示:
注2:在可執行檔p2中沒有拷貝任何libvector.so真正的代碼和資料節,而是由連結器拷貝了一些重定位和符號表資訊,它們使得運行時動態連結器可以解析libvector.so中代碼和資料的引用,重定位完成連結任務。其中需要重定位的有:
- 1)重定位libc.so的文本和資料到某個儲存空間段;
- 2)重定位libvector.so的文本和資料到另一個儲存空間段;
- 3)重定位p2中所有對libc.so和libvector.so定義的符號的引用。
最後連結器將控制傳遞給應用程式。
從這個時刻開始,共用庫的位置就固定了,並在在程式的執行過程中都不會再改變!
2-B:應用程式運行過程中動態連結和載入共用庫
2-B.1 概念:與A情況不同,此情況下:應用程式
在運行過程中要求動態連結器載入和連結任意共用庫,而無需編譯時間連結那些庫到應用中。
2-B.2 應用執行個體Linux系統為應用程式在運行過程中載入和連結共用庫提供了一組API:
[cpp] view plaincopyprint?
- #include<dlfcn.h>
-
- /* 載入和連結共用庫 filename
- filename:共用庫的名字
- flag有:RTLD_LAZY, RTLD_NOW,二者均可以和RTLD_GLOBAL表示取或
- */
- void *dlopen(const char *filename, int flag); // 若成功則返回執行控制代碼的指標,否則返回NULL
-
- /*根據共用庫操作控制代碼與符號,返回符號對應的地址
- handle:共用庫操作控制代碼
- symbol:需要引用的符號名字
- */
- void *dlsym(void *handle, char *symbol); // 若成功則返回執行符號的指標(即地址),若出錯則返回NULL
-
- /* 如果沒有程式正在使用這個共用庫,卸載該共用庫 */
- int dlclose(void *handle); // 若卸載成功,則返回0,否則返回-1
-
- /* 捕捉最近發生的錯誤 */
- const char *dlerror(void); // 若前面對dlopen,dlsym或dlclose調用失敗,則返回錯誤訊息,否則返回NULL
例子:
[cpp] view plaincopyprint?
- #include <stdio.h>
- #include <stdlib.h>
- #include <dlfcn.h>
-
- int x[2] = {1, 2};
- int y[2] = {3, 4};
- int z[2] ={0};
-
- int main()
- {
- void *handle;
- void (*addvec)(int *, int *, int *,int);
- char *error;
-
- handle = dlopen("./libvector.so", RTLD_LAZY);
- if(!handle){
- fprintf(stderr, "%s\n", dlerror());
- exit(1);
- }
-
- addvec = dlsym(handle, "addvec");
- if((error = dlerror()) != NULL){
- fprintf(stderr, "%s\n", dlerror());
- exit(1);
- }
-
- addvec(x, y, z, 2);
- printf("z = [%d %d]\n", z[0], z[1]);
-
- if(dlclose(handle) < 0){
- fprintf(stderr, "%s\n", dlerror());
- exit(1);
- }
-
- return 0;
- }
運行結果:-ldl參數:表示產生的對象模組需要用到共用庫
Referebces:
1.《深入理解電腦系統》第7章:連結 P448-P479
2. 靜態庫、共用庫、動態庫的建立和使用 :http://bbs.chinaunix.net/thread-2037617-1-1.html
3. Linux 動態庫剖析:http://www.ibm.com/developerworks/cn/linux/l-dynamic-libraries/
4. dlopen: http://baike.baidu.com/link?url=VswI42A-IxFuF5SelbJxDREXuY0BvYWHEdcCYozSNH93ark0nTMi4YdhHrvt-bIo2_F-swU2onuYMNwXeUGVMq
【轉】Linux 靜態庫與共用庫的使用