做實(shí)驗(yàn)網(wǎng)站企業(yè)員工培訓(xùn)內(nèi)容及計(jì)劃
動態(tài)庫
概述
動態(tài)庫的擴(kuò)展名是.so。
動態(tài)庫是被加載,調(diào)用的時(shí)候是根據(jù)內(nèi)存地址去調(diào)用,而不是將代碼復(fù)制到文件中。
動態(tài)庫可以同時(shí)被多個(gè)進(jìn)程使用。
實(shí)戰(zhàn)案例:構(gòu)建 libmath.so 動態(tài)庫
準(zhǔn)備源文件
calc.h
- 定義加法:
int add(int a, int b);
- 定義減法:
int sub(int a, int b);
#ifndef __CALC_H_
#define __CALC_H_int add(int a, int b);
int sub(int a, int b);#endif // __CALC_H_
calc.c
- 簡單的實(shí)現(xiàn)加法
- 簡單的實(shí)現(xiàn)減法
#include "calc.h"int add(int a, int b){return a + b;
}int sub(int a, int b){return a - b;
}
show.h
#ifndef __SHOW_H_
#define __SHOW_H_void show(int a, char* op, int b, int res);#endif // __SHOW_H_
show.c
#include <stdio.h>
#include "show.h"void show(int a, char* op, int b, int res){printf("%d %s %d = %d\n", a, op, b, res);
}
編譯C源文件
gcc -c -fpic calc.c
gcc -c -fpic show.c
構(gòu)建動態(tài)庫
gcc -shared calc.o show.o -o libmath.so
使用動態(tài)庫
main.c
#include <stdio.h>
#include "calc.h"
#include "show.h"int main(){int a = 11;int b = 22;int res = add(a, b);show(a, "+", b, res);return 0;
}
編譯并運(yùn)行文件,此時(shí)把靜態(tài)庫文件也帶上:
# 先配置庫所在的環(huán)境變量
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.# 編譯執(zhí)行
gcc main.c libmath.so -o main && ./main
輸出結(jié)果如下:
11 + 22 = 33
動態(tài)加載常用方法
dlopen 方法
語法:
void* dlopen(char const** filename, int flag)
功能:將動態(tài)共享庫載入內(nèi)存并獲得其訪問句柄。
參數(shù):
- filename:動態(tài)庫路徑,若只給文件名不帶目錄,則根據(jù)LD_LIBRARY_PATH環(huán)境變量的值搜索動態(tài)庫
- flag:
- RTLD_LAZY:延遲加載,使用動態(tài)庫中的符號時(shí)才真的加載到內(nèi)存
- RTLD_NOW:立即加載
返回值:成功返回動態(tài)庫的訪問句柄,失敗返回NULL。
句柄:唯一標(biāo)識了系統(tǒng)內(nèi)核所維護(hù)的動態(tài)共享庫對象,將作為后續(xù)函數(shù)調(diào)用的參數(shù)。
dlclose 方法
語法:
int dlclose(void * handle)
功能:從內(nèi)存中卸載動態(tài)庫
參數(shù):handle,動態(tài)句柄
返回值:成功返回0,失敗返回非0
所卸載的共享庫未必真的會立即從內(nèi)存中消失,因?yàn)槠渌绦蚩赡苓€需要使用該庫。只有所有使用該庫的程序都顯式或者隱式的卸載了該庫,該庫所占用的內(nèi)存空間才會真正得到釋放。
無論卸載的共享庫是否真正被釋放,傳遞給close函數(shù)的句柄都會在該函數(shù)成功返回以后立即失效。
dlerror 方法
語法:
char* dlerror(void)
功能:獲取在加載,使用和卸載共享庫過程中所發(fā)生的錯(cuò)誤。
返回值:有錯(cuò)誤則返回指向錯(cuò)誤信息字符串的指針,否則返回NULL。
使用示例:
void* handle = dlopen("libmath.so", RTLD_NOW);
if (!handle){fprintf(stderr, "dlopen: %s\n", dlerror());exit(EXIT_FAILURE);
}
dlsym 方法
語法:
void* dlsym(void* handle, char const* symbol)
功能:從已被加載的動態(tài)庫中獲取特定名稱的符號地址
參數(shù):
- handle 動態(tài)庫訪問句柄
- symbol 符號名
返回值:成功返回給定符號的地址,失敗返回NULL
該函數(shù) 所返回的指針為void*類型,需要轉(zhuǎn)型為實(shí)際目標(biāo)類型相一致的指針后才能使用。
實(shí)戰(zhàn):動態(tài)加載 libmath.so 動態(tài)庫
準(zhǔn)備 .so 文件:
mkdir lib
mv libmath.so ./lib/
ls lib
編寫main.c
#include <stdio.h>
#include <dlfcn.h>int main(){// get the .so handlevoid* handle = dlopen("./lib/libmath.so", RTLD_NOW);if(handle == NULL){fprintf(stderr, "dlopen error: %s\n", dlerror());return -1;}// get the func addressint (*add)(int, int) = dlsym(handle, "add");if(add == NULL){fprintf(stderr, "dlsym error: %s\n", dlerror());return -1;}// use the add funcint value = add(11, 22);printf("11 + 22 = %d\n", value);// close the handleif(dlclose(handle)){fprintf(stderr, "dlclose error: %s\n",dlerror());return -1;}return 0;
}
編譯并執(zhí)行程序:
gcc -o main main.c -ldl && ./main
輸出:
11 + 22 = 33