轉帖:靜態鏈接庫與動態鏈接庫

一、靜態鏈接庫
靜態鏈接庫的代碼在編譯時鏈接到應用程序中,因此編譯時庫必須存在,並且需要通過 “-L” 參數傳遞給編譯器,應用程序執行時不需要靜態庫的存在。
靜態庫的生成
靜態庫的生成分三步,設計庫原碼、編譯.o文件和使用ar命令生成庫。
1. 設計庫原碼。

//****pt1.c***/
#include <stdio.h>
int pt1(void)
{
printf("I am print1./n");
return 0;
}
//****pt2.c***/
#include <stdio.h>
int pt2(void)
{
printf("I am print2./n");
return 0;
}

2. 編譯.o
使用 –c 選項產生目標文件,使用 –O選項優化代碼;

# gcc –c –O pt1.c pt2.c
3. 使用ar庫
創建靜態庫libpt.a, 並通過 ar 的 “-r” 選項增加中間目標文件到靜態庫文件中.

#ar –rsv libpt.a pt1.o pt2.o
4. 靜態庫的命名規則
爲了在編譯中正確的查找到庫文件,表態庫必需以 lib[name].a 的規則命名,如libpt.a.
靜態庫的使用
1. 調用庫代碼

/****main.c*******/
int main(void)
{
pt1();
pt2();
return 0;
}
2. 編譯鏈接選項

#gcc –O –o main main.c –L ./-lp
或:

#gcc –O –o main main.c ./libpt.a
其中 –lpt的含義是 libpt.a, “-L ./” 含義是可以在當前目標下查找庫文件。
3. 執行目標文件

#./main
Ar命令詳解
Ar [drqtpmx] [options] archivefile objfile1 objfile2 …..
參數說明:
-r 將objfile文件插入靜態庫尾或替換靜態庫中的同名文件
-x 從靜態庫文件中抽取庫文件objfile
-t 打印靜態庫中的文件列表
-d 從靜態庫中刪除文件objfile
-s 重置靜態庫文件索引
-v 創建文件的冗餘信息
-c 創建靜態文件庫


二、動態庫
動態庫也稱爲共享庫,其代碼不會鏈接到目標文件中,只有當動態庫能正確訪問時,應用程序才能正確的執行動態鏈接庫函數。應用程序執行動態鏈接庫有兩種方式:隱式調用和顯式調用。
1. 隱式調用
也稱爲共享庫的靜態加裁,其中動態庫函數會在應用程序執行開始時加入內存,在應用程序執行完畢後自動卸載。隱式調用的編譯方式與靜態庫一致。隱式調用的動態庫必須放在特定的目錄內才能被執行。
2. 顯式調用
顯式調用也稱爲動態庫的動態加裁,編譯時可以不顯示的提供原動態庫文件入名稱,但必須遵循dlopen等函數的規則實現調用。應用程序可以在其運行的任意時刻加載或卸載動態鏈接庫。
動態庫的生成
動態庫的生成主要包括三步:設計庫原碼、編譯位置無關碼(PIC)型.o文件、鏈接動態庫。鏈接動態庫的命令包括特殊標誌,與鏈接靜態庫和鏈接可執行動態文件有區別,不同的UNIX系統也不盡相同。編譯PIC型.o文件的方法一般採用C語言編譯器的 “-KPIC” 或者 “-fpic”選項,創建最終動態庫的方法一般是採用C語言編譯器的 “-G”可者 “-shared”選項,或者直接使用工具ld創建。


1. 設計庫原碼。

/*pr1.c*/
#include <stdio.h>
int p=1;
void print(void)
{
printf("this is the first dll src./n");
}
/*pr2.c*/
#include <stdio.h>
int p=2;
void print(void)
{
printf("this is the second dll src./n");
}
2. 編譯位置無關碼(PIC)型.o文件和鏈接動態庫(linux和其它使用gcc編譯器的UNIX下)

#gcc –fpic –c pr1.c pr2.c
#gcc –shared –o pr1.so pr1.o
#gcc –shared –o pr2.so pr2.o
或者:

#gcc –O –fpic –shared –o pr1.so pr1.c
#gcc –O –fpic –shared –o pr2.so pr2.c
某些版本gcc 也可以使用 “-G”替換”-shared”;


動態鏈接庫的隱式調用
動態鏈接庫的隱式調用分三個步驟:調用庫函數代碼、編譯鏈接、動態庫查找
1. 調用庫函數

/*td1.c*/
int main(void)
{
print();
return 0;
}
2. 編譯鏈接選項
可直接將自建動態庫文件名作爲參數傳遞給編譯器。

#cp pr1.so dll.so
#gcc –O –o td1 td1.c ./dll.so
#./td1
如果無法執行dll.so中的print,可將pr2.so COPY 成dll.so,再執行;
2. 動態鏈接庫的查找
試着將dll.so刪除,再可執行 ./td1,則提示夫法找到相應庫,

#rm dll.so
#./td1
Dynamic linker: …….
Killed
當需要載入動態庫時,UNIX會按一定的方法查找相關的庫,上述中UNIX無法定位庫文件,所以出錯。一般情況下,當前目錄是默認查找目標,但有些UNIX系統默認目錄不是當前目錄,這樣庫文件在當前目錄下也會提示出錯。解決方法爲:
1)。帶路徑編譯

#mkdir dll
#cp pr1.so ./dll/dll.so
#gcc –O –o td1 td1.c . ./dll/dll.so
當程序執行時會自動到./dll下查找相關庫文件。
2)。更改環境變量
UNIX遍歷某個特定環境變量的存儲路徑,來查找動態庫,用戶可以修改環境變量以達到自動搜索動態庫的目的。

#LD_LIBRARY_PATH = ./:dll
#export LD_LIBRARY_PATH
#./td1
注:不同的UNIX所依賴的動態庫查找路徑環境變量名稱不相同;
3. 動態庫的更換

#cp pr2.so ./dll/dll.so
#./td1
this is the second dll src.
動態鏈接庫的顯式調用
顯式中,應用程序應該遵循dlopen函數的規則調用動態鏈接庫代碼,依次爲打開動態庫、獲取動態對象地址、調用動態庫對象、錯誤檢查、關閉動態庫。
相關函數族:
1.UNIX中使用dlopen打開動態庫:

#include<dlfcn.h>
void *dlopen(const char *pathname, int mode);
dlopen加載動態庫,成功時返回指向動態庫的句柄,失敗時返回NULL,參數說明如下:
pathname 帶路徑的動態庫名
mode 加載方式,可取值:
RTLD_LAZY: 動態庫的對象符號在被調用時解釋
RTLD_NOW:動態庫的對象所有符號在函數dlopen返回前被解釋;
2. UNIX中使用dlsym取得動態庫中對象地址

#include<dlfcn.h>
void *dlsym(void *handle, const char * name);
dlsym在打開的動態庫中搜索給爲 name的函數對象或全變量,成功則返回相關地址,失敗返回NULL,由於返回的是 void *類型指針,需要類型轉換。參數說明如下:
handle 打開的動態庫句柄
name 查找的全局變量名或函數名
3. 錯誤檢查 UNIX中使用dlerror顯式動態庫操作中的錯誤信息

#include<dlfcn.h>
char *dlerror(void)
本函數爲返回最近的錯誤信息,函數執行後自動將錯誤信息置爲NULL
4. 關閉動態庫 動態庫使用完後,要關閉,以釋放內存

#include<dlfcn.h>
int dlclose(void * handle);
參數說明:
handle 爲打開的動態庫
應用示例:
以下程序動態的調用以上述創建的動態鏈接庫

/****/
#include<dlfcn.h>
char errs_msg[100];
void main(void)
{
void *pHandle;
void (*pFunc)();
int *p;
pHandle = dlopen("./dll.so",RTLD_NOW);
if (NUll == pHandle)
{
strcpy(errs_msg,"can not find dll.so");
goto ERR;
}
pFunc = (void(*)()) dlsym(pHandle,"print");
if (NUll != pFunc)
{
pFunc();
}else
{
strcpy(errs_msg,"can not find print");
goto ERR;
}
p = (int *)dlsym(pHandle,"p");
if (p)
{
printf("p=%d/n",p);
}else
{
strcpy(errs_msg,"can not find p./n");
goto ERR;
}
dlclose(pHandle);
return 0;
///err handle
ERR:
perror(errs_msg);
dlclose(pHandle);
exit(1);

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章