1. 程式人生 > >Linux編程中鏈接庫的使用

Linux編程中鏈接庫的使用

linu erro return 指針 什麽 大型軟件 linux編程 鏈接 文件處理

鏈接庫本質上是一段可執行的二進制代碼,可以被操作系統載入內存執行。按加載的時機不同,鏈接庫可以分為靜態鏈接庫和動態鏈接庫。 靜態鏈接庫:編譯過程中加載進可執行文件的庫(靜態庫省去了運行時加載的消耗,但會導致可執行文件體積增大)
動態鏈接庫:程序運行過程中,動態加載進內存的庫(動態庫加載需要資源消耗,但可以顯著降低可執行文件體積) 什麽情況下使用鏈接庫?
1、大型軟件項目中,不同模塊可以各自完成,然後封裝成鏈接庫供上層模塊調用。
2、一些通用的功能,如文件處理、數據庫接口、算法等,可以封裝成庫,從而避免每個模塊都維護一份獨立的通用代碼。
3、項目中有很多小的業務模塊,可以把各模塊制作成統一接口的動態庫,主進程根據實際業務按需加載。 靜態庫的使用,主要有兩點:
一是在主程序裏需有靜態庫接口函數的聲明,一般使用一個頭文件;
二是在編譯時加載靜態庫,如linux gcc編譯時可以用形如-lcpplib的選項來加載一個文件名為libcpplib.a或libcpplib.so的庫。
這裏說明下命名規範,一般建議靜態庫的後綴用.a,動態庫後綴用.so。
滿足上面兩點,靜態庫裏的函數就可以像平常一樣直接使用了。 動態庫的使用,相對要復雜一些。
這裏拋個問題,既然是動態加載的,主程序怎麽知道裏面有什麽函數,怎麽調用呢?
在linux系統裏,可以nm查看鏈接庫的符號表,也即是裏面的函數表。Linux程序的動態庫調用,也提供了4個加載動態庫相關的函數:dlopen、dlsym、dlerror和dlcolose。
#include <dlfcn.h>/*包含的頭文件*/
/*pathname為動態庫文件名;mode是打開方式,如RTLD_NOW表示直接解析出動態庫中所有的符號*/
/*此函數返回一個void指針指向加載的庫的句柄*/
void * dlopen( const char * pathname, int mode);
/*handle為dlopen返回的句柄,symbol為函數名*/
/*此函數返回庫裏指定名稱的函數的指針*/
void*dlsym(void*handle,constchar*symbol);
/*此函數在加載鏈接庫出錯時,返回錯誤信息*/
char *dlerror(void);
/*關閉指定的動態庫句柄*/
int dlclose (void *handle); 下面是演示用的代碼:
該段代碼實現一個動態庫調用接口DllRun,通過向該接口傳遞業務數據、庫名、函數名,可以指定相應的庫函數來處理數據。
同時iFlag控制該動態庫在使用完後是否立即釋放,如果不釋放(頻繁使用的庫不釋放可以節省加載的開支),則將庫句柄指針壓入MAP。
#include <dlfcn.h> typedef int (*dll_func)(char*);/*動態庫的函數聲明形式*/
map<const char*, dll_func> mapFunc;/*保存不釋放的動態庫*/ /*
函數功能:加載動態庫並執行相應的業務函數
輸入參數:strData - 業務數據,根據實現業務情況選擇用什麽方式傳遞業務數據,比如數據多且可以按字段劃分,可以用map
strDllName - 需要加載的庫名稱
strFunc - 業務的函數名稱
iFlag - 加載方式:
0 - 第1次加載後Handle保存到map,之後調用不重新加載
1 - 每次加載均釋放掉,下次重新加載
輸出參數:strData - 函數執行後輸出數據
返回值:0 - 成功;其他失敗
*/
int DllRun(char* strData, const char* strDllName, const char* strFunc, int iFlag)
{
int iRet=-1;
dll_func pFunc=NULL;/*業務函數的指針*/
void* pHandle=NULL;

if(NULL == strFunc)
{
iRet = -1;
goto DllRun_RETURN;
}

if(!iFlag)
{
pHandle = mapFunc[strDllName];
}

if(NULL == pHandle)
{ /*加載動態庫*/
pHandle = dlopen(strDllName, RTLD_NOW);
if(NULL == pHandle)
{
printf("加載鏈接庫[%s]失敗: %s\n", strDllName, dlerror());
iRet = -2;
goto DllRun_RETURN;
}
}

func = dlsym(pHandle, strFunc);/*獲取業務函數指針*/
if(NULL == func)
{
printf("獲取動態庫[%s]的函數[%s]的指針失敗: %s\n", strDllName, strFunc, dlerror());
iRet = -3;
goto DllRun_RETURN;
}

iRet = func(strData);/*業務執行*/
if(0 != iRet)
{
goto DllRun_RETURN;
} DllRun_RETURN:
if(iFlag)
{
if(NULL != pHandle)
{
dlclose(pHandle);
}
}
else
{
mapFunc[strDllName] = pHandle;
}

return iRet;
}

Linux編程中鏈接庫的使用