Linux下的靜態連結與動態連結
什麼是庫
庫是寫好的現有的,成熟的,可以複用的程式碼。現實中每個程式都要依賴很多基礎的底層庫,不可能每個人的程式碼都從零開始,因此庫的存在意義非同尋常。
本質上來說庫是一種可執行程式碼的二進位制形式,可以被作業系統載入記憶體執行。庫有兩種:靜態庫(.a、.lib)和動態庫(.so、.dll)
所謂靜態、動態是指連結。回顧一下,將一個程式編譯成可執行程式的步驟:
靜態庫
之所以成為【靜態庫】,是因為在連結階段,會將彙編生成的目標檔案.o與引用到的庫一起連結打包到可執行檔案(.out)中。因此對應的連結方式稱為靜態連結。
試想一下,靜態庫與彙編生成的目標檔案一起連結為可執行檔案,那麼靜態庫必定跟.o檔案格式相似。其實一個靜態庫可以簡單看成是一組目標檔案(.o 檔案)的集合,即很多目標檔案經過壓縮打包後形成的一個檔案。靜態庫特點總結:
1.靜態庫對函式庫的連結是放在編譯時期完成的。
2.程式在執行時與函式庫再無瓜葛,移植方便。
3.浪費空間和資源,因為所有相關的目標檔案與牽涉到的函式庫被連結合成一個可執行檔案
下面編寫一些簡單的四則運算C++類,將其編譯成靜態庫給他人用,標頭檔案如下所示:
class StaticMath
{
public:
StaticMath(void);
~StaticMath(void);
static double add(double a, double b);//加
static double sub(double a, double b);//減
static double mul(double a, double b);//乘
static double div(double a, double b);//除
};
參考學習:C++中的static關鍵字的總結 這個挺重要的,建議看一下!
Linux下使用 ar 工具,將目標檔案壓縮到一起,並且對其進行編號和索引,以便於查詢和檢索。一般建立靜態庫的步驟如圖所示:
Linux下建立與使用靜態庫
Linux靜態庫命名規則
Linux靜態庫命名規範,必須*是”lib[your_library_name].a”:lib為字首,中間是靜態庫名,副檔名為 .a
建立靜態庫(.a)
通過上面的流程可以知道,Linux建立靜態庫過程如下:
- (1)首先,將程式碼檔案編譯成目標檔案.o(StaticMath.o)
//這是StaticMath.cpp 檔案
#include<iostream>
#include"myhead.h"
using namespace std;
double StaticMath::add(double a,double b)
{
return a+b;
}
double StaticMath::sub(double a,double b)
{
return a-b;
}
double StaticMath::mul(double a,double b)
{
return a*b;
}
double StaticMath::div(double a,double b)
{
return a/b ;
}
g++ -c StaticMath.cpp
注意帶引數-c,將其編譯為.o 檔案,否則直接編譯為可執行檔案
(2)然後,通過 ar 工具將目標檔案打包成 .a 靜態庫檔案
ar -jcv -f libstaticmath.a StaticMath.o
大一點的專案會編寫makefile檔案(CMake等等工程管理工具)來生成靜態庫,輸入多個命令太麻煩了。
- (3) 使用靜態庫
編寫使用上面建立的靜態庫的測試程式碼:
#include "StaticMath.h"
#include <iostream>
using namespace std;
int main(int argc, char* argv[])
{
double a = 10;
double b = 2;
cout << "a + b = " << StaticMath::add(a, b) << endl;
cout << "a - b = " << StaticMath::sub(a, b) << endl;
cout << "a * b = " << StaticMath::mul(a, b) << endl;
cout << "a / b = " << StaticMath::div(a, b) << endl;
return 0;
}
Linux下使用靜態庫,只需要在編譯的時候,指定靜態庫的搜尋路徑(-L選項)、指定靜態庫名(不需要lib字首和.a字尾,-l選項)。
g++ TestStaticLibrary.cpp -L../StaticLibrary -lstaticmath
-L:表示要連線的庫所在目錄
-l (小寫L):指定連結時需要的動態庫,編譯器查詢動態連線庫時有隱含的命名規則,即在給出的名字前面加上lib,後面加上.a或.so來確定庫的名稱。
gcc 引數簡介
-E:只執行到預處理階段,不生成任何檔案
-S:將C程式碼轉換為彙編程式碼(.s 彙編檔案)
-c:僅執行編譯操作,不進行連線操作(.o 機器碼)
-o:指定生成的輸出檔案(.out 可執行檔案)
-L:告訴gcc去哪裡找庫檔案。 gcc預設會在程式當前目錄、/lib、/usr/lib和/usr/local/lib下找對應的庫
-l:用來指定具體的靜態庫、動態庫是哪個
-I: 告訴gcc去哪裡找標頭檔案
動態庫
通過上面的介紹發現靜態庫,容易使用和理解,也達到了程式碼複用的目的,那為什麼還需要動態庫呢?
為什麼還需要動態庫?
為什麼需要動態庫,其實也是靜態庫的特點導致。
1.空間浪費是靜態庫的一個問題。
2.另一個問題是靜態庫對程式的更新、部署和釋出頁會帶來麻煩。如果靜態庫liba.lib更新了,所以使用它的應用程式都需要重新編譯、釋出給使用者(對於玩家來說,可能是一個很小的改動,卻導致整個程式重新下載,全量更新)
動態庫在程式編譯時並不會被連線到目的碼中,而是在程式執行是才被載入。不同的應用程式如果呼叫相同的庫,那麼在記憶體裡只需要有一份該共享庫的例項,規避了空間浪費問題。動態庫在程式執行是才被載入,也解決了靜態庫對程式的更新、部署和釋出頁會帶來麻煩。使用者只需要更新動態庫即可。
動態庫特點總結:
1.動態庫把對一些庫函式的連結載入推遲到程式執行的時期。
2.可以實現程式之間的資源共享。(因此動態庫也稱為共享庫)
3.將一些程式升級變得簡單。甚至可以真正做到連結載入完全由程式設計師在程式程式碼中控制(顯式呼叫)。
Linux下建立與使用動態庫
Linux動態庫的命名規則
動態連結庫的名字形式為 libxxx.so,字首是lib,字尾名為“.so”
建立動態庫(.so)
編寫四則運算動態庫程式碼
// myhead.h 檔案
class DP
{
public:
static void print_111();
static void print_222();
static void print_333();
};
//print_333 檔案(print_111和print_222 檔案與print_333 檔案類似)
#include<iostream>
#include"myhead.h"
using namespace std;
void DP::print_333()
{
cout << "333333333333333" << endl ;
}
首先,生成目標檔案,此時要加編譯器選項-fpic
g++ -fPIC -c print_*.cpp
-fPIC 建立與地址無關的編譯程式(pic,position independent code),是為了能夠在多個應用程式間共享。
然後,生成動態庫,此時要加連結器選項 -shared
g++ -shared -o libtest.so print_*.o
-shared 指定生成動態連結庫。
其實上面兩個步驟可以合併為一個命令:
g++ -fPIC -shared -o libtest.so print_*.cpp
生成libtest.so 動態庫
使用動態庫
編寫使用動態庫的測試程式碼:
// testDP.cpp 檔案
#include "myhead.h"
#include <iostream>
using namespace std;
int main(void)
{
DP::print_111();
DP::print_222();
DP::print_333();
return 0;
}
引用動態庫編譯成可執行檔案(跟靜態庫方式一樣):
g++ testDP.cpp -L./ -ltest
然後執行:./a.out,報錯如下:
這是由於程式執行時沒有找到動態連結庫造成的。程式編譯時連結動態連結庫和執行時使用動態連結庫的概念是不同的,在執行時,系統能夠知道其所依賴的庫的名字,但是還需要知道絕對路徑。有幾種辦法可以解決此種問題:
1. 因為系統會按照 LD_LIBRARY_PATH 環境變數來查詢除了預設路徑之外( /lib, /usr/lib, /usr/local/lib)的共享庫(動態連結庫)的其他路徑,就像PATH變數一樣!所以我們可以修改該環境變數來解決這個問題。
export LD_LIBRARY_PATH=/home/hp/LinuxC:$LD_LIBRARY_PATH
2. 將動態連結庫賦值一份到預設路徑
sudo cp libtest.so /usr/lib
3. 因為系統中的配置檔案/etc/ld.so.conf是動態連結庫的搜尋路徑配置檔案,在程式執行時會去讀取該檔案,那麼我們就將我們自己編寫的庫的路徑寫到該配置檔案中去即可。
在最後一行加入 /home/liushengxi/C-/自建庫/test
執行ldconfig ,該命令會重建/etc/ld.so.cache檔案
idconfig命令查詢:命令查詢
至此,基本連結就是這樣的了~_~
相關文章
- 動態連結庫與靜態連結庫
- linux下靜態連結庫和動態連結庫的區別有哪些Linux
- 【連結 1】與靜態連結庫連結
- 靜態連結動態連結的連結順序問題和makefile示例
- Linux環境下:程式的連結, 裝載和庫[靜態連結]Linux
- cmake 連結動態連結庫
- 動態連結的PLT與GOTGo
- 靜態連結之深度解剖
- 理解靜態繫結與動態繫結
- java中的靜態繫結與動態繫結Java
- linux下的靜態庫與動態庫Linux
- 動態連結的相關結構
- 動態連結的步驟與實現
- C#資料結構-靜態連結串列C#資料結構
- 使用js動態新增連結隨機連結JS隨機
- 在AndroidStudio下使用cmake編譯出靜態連結庫的方法Android編譯
- 關於動態連結串列的理解
- 動態連結串列的建立(程式碼)
- C++的動態繫結和靜態繫結C++
- 在 Linux中如何使用動態連結模組庫?Linux
- [pwn基礎]動態連結原理
- 用動態連結動態洩露system地址並利用
- 動態連結庫的生成和使用(二)
- 動態連結庫(DLL)的建立和使用
- Linux系統 g++ 連結 libopencv_world.a 靜態庫編譯程式LinuxOpenCV編譯
- Windows環境下,動態連結庫(DLL)的“匯入”與“匯出”概念Windows
- Gazebo新增模型並控制模型運動作為動態障礙物(Ubuntu16.04, Gazebo7.16),附錄動態連結庫和靜態連結庫區別模型Ubuntu
- jQuery動態修改連結的href屬性值jQuery
- 簡述Linux下的靜態庫和動態庫Linux
- 載入動態連結庫——dlopen dlsym dlclose
- C#呼叫C++動態連結庫C#C++
- Linux下快速靜態編譯Qt以及Qt動態/靜態版本共存Linux編譯QT
- 《程式設計師的自我修養筆記之靜態連結》程式設計師筆記
- 【技術向】Linux動態連結庫預載入型後門Linux
- Linux常用基本命令(軟連結與硬連結 )Linux
- 如何連結兩個名字一樣動態庫
- Linux軟連結和硬連結Linux
- linux硬連結和軟連結Linux
- 資料結構-malloc申請動態空間-連結串列的建立資料結構