深入理解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第七章和《程式設計師的自我修養》
相關文章
- 動態連結庫與靜態連結庫
- linux下靜態連結庫和動態連結庫的區別有哪些Linux
- 【連結 1】與靜態連結庫連結
- 靜態庫與動態庫
- C#呼叫C++動態連結庫C#C++
- ios靜態庫和動態庫iOS
- cmake 連結動態連結庫
- 動靜態庫
- cmake:生成靜態庫和動態庫
- Linux共享庫、靜態庫、動態庫詳解Linux
- linux下的靜態庫與動態庫Linux
- [Linux]動靜態庫Linux
- 理解靜態繫結與動態繫結
- 深入理解靜態代理與JDK動態代理JDK
- linux 動態庫 靜態庫 函式覆蓋Linux函式
- android下java的靜態庫和動態庫AndroidJava
- iOS動態庫和靜態庫的運用iOS
- C靜態庫的建立與使用--為什麼要引入靜態庫?
- P/Invoke之C#呼叫動態連結庫DLLC#
- lua——alien庫實現lua呼叫C動態連結庫(dll、so)
- 一、靜態庫和動態庫,Makefile專案管理專案管理
- 簡述Linux下的靜態庫和動態庫Linux
- 筆記: 判斷lib庫是動態庫還是靜態庫筆記
- Gazebo新增模型並控制模型運動作為動態障礙物(Ubuntu16.04, Gazebo7.16),附錄動態連結庫和靜態連結庫區別模型Ubuntu
- Android NDK祕籍--淺析靜態庫和動態庫Android
- iOS中的動態庫,靜態庫和framework介紹iOSFramework
- C++動態庫封裝C#庫,驗證動態庫環境C++封裝C#
- 靜態庫生成
- Linux環境下:程式的連結, 裝載和庫[靜態連結]Linux
- C++的動態繫結和靜態繫結C++
- Android NDK祕籍--編譯靜態庫、呼叫靜態庫Android編譯
- JNI呼叫c動態連結庫函式程式碼實踐函式
- C語言--靜態區域性變數C語言變數
- C語言動態庫libxxx.so的幾種使用方法C語言
- 靜態連結動態連結的連結順序問題和makefile示例
- 載入動態連結庫——dlopen dlsym dlclose
- 動態連結庫的生成和使用(二)
- 動態連結庫(DLL)的建立和使用
- C#資料結構-靜態連結串列C#資料結構