深入理解C語言----動態庫 & 靜態庫 & 連結
庫是程式碼共享的主要方式,動態庫和靜態庫的主要區別在於他們連結形式的不同(靜態和動態連結),它們都是目標檔案的集合,再加上一些索引表項來表徵各檔案的資訊。通常,linux裡目標檔案是ELF格式,而win則為PE
靜態庫和靜態連結
linux下靜態庫是以.a為字尾,而win下靜態庫是以lib為字尾。
靜態連結是由連結器將一個或多個目標檔案及靜態庫中所被引用的目標檔案完全連結到一個可執行檔案中,由連結器完成所有的工作,包括符號解析和重定位。
該可執行檔案直接由駐留在記憶體裡的載入器載入執行即可。
用一個小的靜態庫作為例子:
queue.h
#ifndef _QUEUE_H_
#define _QUEUE_H_
int queue[16] = {0};
int head = 0;
int rear = 0;
#endif
append.c
extern int queue[16];
extern int head;
extern int rear;
int append(int ele)
{
if ((rear + 1) % 16 == head)
return 0;
else
{
rear = (rear+1) % 16;
queue[rear] = ele;
return 1;
}
}
serve.c
extern int queue[16];
extern int head;
extern int rear;
int serve()
{
if (head != rear )
{
head = (head + 1) % 16;
return queue[head];
}
else
return -1;
}
main.c
#include<stdio.h>
#include"queue.h"
extern int head;
extern int rear;
int main()
{
int i;
for (i = 1; i <= 20; i++)
{
if (!append(i))
{
printf("%d ",serve());
i--;
}
}
while (head != rear)
printf("%d ",serve());
return 0;
}
首先,我們把兩個實現的函式編譯為目標檔案:
gcc -c append.c
gcc -c serve.c
然後用ar工具打包為
ar rcs libqueue.a append.o serve.o
靜態庫選項r 表示將後面的檔案列表新增到檔案包,如果檔案包不存在就建立它,如果檔案包中已有同名檔案就替換成新的。c表示建立一個庫,s 是專用於生成靜態庫的,表示為靜態庫建立索引,這個索引被連結器使用。ranlib 命令也可以為靜態庫建立索引,以上命令等價於:
ar rc libqueue.a append.o serve.o ranlib libqueue.a
最後即可生存我們的主程式:
gcc -o queue main.c ./libqueue.a
動態庫和動態連結
linux裡動態庫以so作為字尾,而win裡則是dll
動態連結從某個角度說,可以說是把靜態連結的過程進行了拆分。當建立可執行檔案時,靜態執行一些連結,然後在程式載入時,動態完成連結過程。程式執行時,由載入器呼叫動態連結器,動態連結器執行一些重定位之後,才把控制權返回給應用程式。
動態庫的生成很簡單:
gcc -shared -fPIC libqueue.so append.c serve.c
編譯時連結上該庫
gcc -o queue main.c ./libqueue.so
與靜態連結相比,動態連結有很多優勢,比如節省了系統資源開銷,方便軟體升級更新。
動態裝載庫
還有一種更靈活的模組載入方式,在程式執行的時候進行載入和連結,即:顯式執行時連結。它的優勢在於不必重新啟動程式,並且減少了程式的啟動時間和記憶體使用。從格式上看它和動態庫沒有區別,區別在於,它的裝載是通過一系列由動態連結器提供的API,具體是dlopen,dlsym,dlerror,dlclose,定義在dlfen.h中,實現在/lib/libdl.so.2裡。關於這幾個函式的使用可以檢視相關文件。於是,上面的例子也可以實現為:main2.c
#include<stdio.h>
#include<dlfcn.h>
#include"queue.h"
extern int head;
extern int rear;
int main()
{
void *handle;
int (*serve)();
int (*append)(int);
char *error;
int i;
if ((handle = dlopen("./libqueue.so",RTLD_LAZY))== NULL)
{
printf("Fail to load dynamic lib!\n");
return 1;
}
serve = dlsym(handle, "serve");
if ((error = dlerror()) != NULL)
{
printf("symbol serve not found!\n");
return 1;
}
append = dlsym(handle, "append");
if ((error = dlerror()) != NULL)
{
printf("symbol append not found!\n");
return 1;
}
for (i = 1; i <= 20; i++)
{
if (!append(i))
{
printf("%d ", serve());
i--;
}
}
while (head != rear)
printf("%d ", serve());
dlclose(handle);
return 0;
}
注意編譯時帶上-rdynamic引數並且連結libdl.so:
gcc -rdynamic -o queue main.c -ldl
關於連結和庫是一個很大知識範圍,涉及較多的底層知識,比如上文很簡略的動態連結中的很多概念如延遲繫結,位置無關程式碼,每一個都值得理解和研究。篇幅和能力所限,本文只以一個具體的例子實現作為學習引子。更多的內容可以參考:CSAPP第七章和《程式設計師的自我修養》
相關文章
- 動態連結庫與靜態連結庫
- C語言編寫靜態連結庫及其使用C語言
- C++靜態庫與動態庫深入研究C++
- 動態連結庫和靜態連結庫的區別
- Win32動態連結庫與靜態連結庫的區別Win32
- 深入理解 C++ 的動態繫結和靜態繫結C++
- C語言的本質(34)——靜態庫C語言
- C語言動態呼叫庫(轉)C語言
- Linux 依賴動態庫 / 靜態庫的動態態庫 / 靜態庫Linux
- linux下靜態連結庫和動態連結庫的區別有哪些Linux
- 【連結 1】與靜態連結庫連結
- 靜態庫與動態庫
- C#呼叫C++動態連結庫C#C++
- ios靜態庫和動態庫iOS
- 動靜態庫
- cmake 連結動態連結庫
- Linux靜態庫和動態庫學習總結Linux
- 動態連結庫(DLL)
- 動態連結庫(轉)
- cmake:生成靜態庫和動態庫
- 理解靜態繫結與動態繫結
- 動態庫和靜態庫的區別
- Linux下的靜態庫、動態庫和動態載入庫Linux
- 深入理解靜態代理與JDK動態代理JDK
- Linux共享庫、靜態庫、動態庫詳解Linux
- iOS動態庫和靜態庫的運用iOS
- 菜鳥教程——iOS動態庫與靜態庫iOS
- linux下的靜態庫與動態庫Linux
- iOS 靜態庫(.a, .framework) 動態庫(.framework, dylib)iOSFramework
- ios靜態庫與動態庫的區別iOS
- Linux下的共享庫(動態庫)和靜態庫Linux
- Linux下的靜態連結與動態連結Linux
- P/Invoke之C#呼叫動態連結庫DLLC#
- golang可以呼叫C++的動態連結庫麼GolangC++
- C靜態庫的建立與使用--為什麼要引入靜態庫?
- linux 動態庫 靜態庫 函式覆蓋Linux函式
- 一、靜態庫和動態庫,Makefile專案管理專案管理
- android下java的靜態庫和動態庫AndroidJava