1.什么是庫(kù)
庫(kù)lib
是編譯好的二進(jìn)制代碼胎食,可以被操作系統(tǒng)載入內(nèi)存執(zhí)行逗宜,一般是預(yù)先編譯好的函數(shù)的集合庆尘,可以通過(guò)頭文件鏈接到庫(kù)文件剃诅,執(zhí)行已經(jīng)編譯好的代碼段。
庫(kù)一般分為靜態(tài)庫(kù)(static lib驶忌,在linux系統(tǒng)一般是.a文件)和動(dòng)態(tài)庫(kù)(dynamic lib矛辕,也叫共享庫(kù),在linux系統(tǒng)一般是.so文件)付魔。二者的不同點(diǎn)在于被載入的時(shí)間不同:
- 靜態(tài)庫(kù)
.a
在編譯的過(guò)程中會(huì)被編譯到可執(zhí)行文件聊品,也就是說(shuō)會(huì)增大可執(zhí)行文件的體積。 - 動(dòng)態(tài)庫(kù)則是在執(zhí)行的過(guò)程中才會(huì)去讀取
.so
文件几苍,不用編譯進(jìn)可執(zhí)行程序翻屈,因此可執(zhí)行程序體積較小。缺點(diǎn)是拷貝代碼時(shí)如果沒(méi)有.so文件可能會(huì)造成無(wú)法執(zhí)行妻坝∩炜簦‘
2.靜態(tài)庫(kù)
靜態(tài)庫(kù)
程序在編譯階段會(huì)把靜態(tài)庫(kù)的內(nèi)容復(fù)制到目標(biāo)文件中惊窖,在鏈接階段將引用的靜態(tài)庫(kù)打包到可執(zhí)行文件中。因此稱為靜態(tài)鏈接赚抡。這里可以發(fā)現(xiàn)爬坑,靜態(tài)鏈接將靜態(tài)庫(kù)直接打包進(jìn)入可執(zhí)行文件纠屋,那么他的組織形式一定和.o文件類似涂臣。
實(shí)際上,一個(gè)靜態(tài)庫(kù)是一組目標(biāo)文件的集合(.o)售担,很多目標(biāo)文件被打包成一個(gè)文件赁遗,并且直接參與鏈接。
靜態(tài)庫(kù)有以下特點(diǎn):
- 1.靜態(tài)庫(kù)對(duì)函數(shù)的鏈接是在編譯時(shí)完成的
- 2.程序在運(yùn)行時(shí)不再需要靜態(tài)庫(kù)族铆,因?yàn)閷?duì)應(yīng)的程序段已經(jīng)被復(fù)制到原來(lái)的位置岩四,移植方便。
- 3.可執(zhí)行文件占用空間較大哥攘,因?yàn)殪o態(tài)庫(kù)內(nèi)容被復(fù)制進(jìn)入了可執(zhí)行文件編譯結(jié)果剖煌。
創(chuàng)建靜態(tài)庫(kù)
- linux靜態(tài)庫(kù)的創(chuàng)建命名規(guī)則:
lib[library_name].a
,lib為前綴,中間是庫(kù)名逝淹,.a為擴(kuò)展名耕姊。 - 創(chuàng)建靜態(tài)庫(kù)
現(xiàn)在有如下代碼,一定要把頭文件和函數(shù)體分開(kāi)聲明栅葡,否則使用靜態(tài)庫(kù)的時(shí)候沒(méi)有頭文件只能猜了哦茉兰。
**
* @file: unite_time.h
* @author: mattbaisteins@gmail.com
* @date: 2020-08-03
* @brif:
**/
#ifndef _UNITE_TIME_H
#define _UNITE_TIME_H
#include <ctime>
#include <sys/time.h>
#include <string>
#include <vector>
#include <cstdlib>
#define BUFFER_SIZE 4096
/*
* get time yymmdd, eg. 190803
* @param void
* @return string now_time
*/
std::string get_time();
#endif // end _UNITE_TIME_H
/**
* @file: unite_time.cpp
* @author: mattbaisteins@gmail.com
* @date: 2020-08-03
* @brif:
**/
#include "unite_time.h"
#include <ctime>
std::string get_time() {
time_t raw_time;
struct std::tm *time_info;
char buffer[BUFFER_SIZE];
std::time(&raw_time);
time_info = std::localtime(&raw_time);
std::strftime(buffer, sizeof(buffer) - 1, "%y%m%d", time_info);
return std::string(buffer);
}
1.首先把代碼先編譯成目標(biāo)文件.o(unite_time.o)
gcc -c unite_time.cpp
2 使用ar工具將目標(biāo)文件打包成靜態(tài)庫(kù).a文件(libunite_time.a)
ar -crv libunite_time.a unite_time.o
注:ar的用法如下
(1)ar是什么
ar命令可以用來(lái)創(chuàng)建、修改庫(kù)欣簇,也可以從庫(kù)中提出單個(gè)模塊规脸。庫(kù)是一單獨(dú)的文件,里面包含了按照特定的結(jié)構(gòu)組織起來(lái)的其它的一些文件(稱做此庫(kù)文件的member)熊咽。原始文件的內(nèi)容莫鸭、模式、時(shí)間戳横殴、屬主黔龟、組等屬性都保留在庫(kù)文件中。
ar命令格式:ar [-] {dmpqrtx} [abcfilNoPsSuvV] [membername] [count] archive files...
例如我們可以用ar rv libtest.a hello.o hello1.o來(lái)生成一個(gè)庫(kù)滥玷,庫(kù)名字是test氏身,鏈接時(shí)可以用-ltest鏈接。該庫(kù)中存放了兩個(gè)模塊hello.o和hello1.o惑畴。選項(xiàng)前可以有‘-'字符蛋欣,也可以沒(méi)有。下面我們來(lái)看看命令的操作選項(xiàng)和任選項(xiàng)∪绱現(xiàn)在我們把{dmpqrtx}部分稱為操作選項(xiàng)陷虎,而[abcfilNoPsSuvV]部分稱為任選項(xiàng)到踏。
【注:ar可讓您集合許多文件,成為單一的備存文件尚猿。在備存文件中窝稿,所有成員文件皆保有原來(lái)的屬性與權(quán)限】
2)參數(shù)介紹 指令參數(shù)
-d 刪除庫(kù)文件中的成員文件。
-m 變更成員文件在庫(kù)文件中的次序凿掂。
-p 顯示庫(kù)文件中的成員文件內(nèi)容伴榔。
-q 將問(wèn)家附加在庫(kù)文件末端。
-r 將文件插入庫(kù)文件中庄萎。
-t 顯示庫(kù)文件中所包含的文件踪少。
-x 自庫(kù)文件中取出成員文件。
選項(xiàng)參數(shù)
a<成員文件> 將文件插入庫(kù)文件中指定的成員文件之后糠涛。
b <成員文件> 將文件插入庫(kù)文件中指定的成員文件之前援奢。
c 建立庫(kù)文件。
f 為避免過(guò)長(zhǎng)的文件名不兼容于其他系統(tǒng)的ar指令指令忍捡,因此可利用此參數(shù)集漾,截掉要放入庫(kù)文件中過(guò)長(zhǎng)的成員文件名稱。
i<成員文件> 將問(wèn)家插入庫(kù)文件中指定的成員文件之前砸脊。
o 保留庫(kù)文件中文件的日期具篇。
s 若庫(kù)文件中包含了對(duì)象模式,可利用此參數(shù)建立備存文件的符號(hào)表脓规。
S 不產(chǎn)生符號(hào)表栽连。
u 只將日期較新文件插入庫(kù)文件中。
v 程序執(zhí)行時(shí)顯示詳細(xì)的信息侨舆。
V 顯示版本信息秒紧。
ar用來(lái)管理一種文檔。這種文檔中可以包含多個(gè)其他任意類別的文件挨下。這些被包含的文件叫做這個(gè)文檔的成員熔恢。ar用來(lái)向這種文檔中添加、刪除臭笆、解出成員叙淌。成員的原有屬性(權(quán)限、屬主愁铺、日期等)不會(huì)丟失鹰霍。實(shí)際上通常只有在開(kāi)發(fā)中的目標(biāo)連接庫(kù)是這種格式的,所以盡管不是茵乱,我們基本可以認(rèn)為ar是用來(lái)操作這種目標(biāo)鏈接庫(kù)(.a文件)的茂洒。
(3)ar的常用用法
創(chuàng)建庫(kù)文件
我不知道怎么創(chuàng)建一個(gè)空的庫(kù)文件。好在這個(gè)功能好像不是很需要瓶竭。通常人們使用“ar cru liba.a a.o"這樣的命令來(lái)創(chuàng)建一個(gè)庫(kù)并把a(bǔ).o添加進(jìn)去督勺。"c"關(guān)鍵字告訴ar需要?jiǎng)?chuàng)建一個(gè)新庫(kù)文件渠羞,如果沒(méi)有指定這個(gè)標(biāo)志則ar會(huì)創(chuàng)建一個(gè)文件,同時(shí)會(huì)給出 一個(gè)提示信息智哀,"u"用來(lái)告訴ar如果a.o比庫(kù)中的同名成員要新次询,則用新的a.o替換原來(lái)的。但是我發(fā)現(xiàn)這個(gè)參數(shù)也是可有可無(wú)的瓷叫,可能是不同版本的ar 行為不一樣吧屯吊。實(shí)際上用"ar -r liba.a a.o"在freebsd5上面始終可以成功。
加入新成員
使用"ar -r liba.a b.o"即可以將b.o加入到liba.a中赞辩。默認(rèn)的加入方式為append雌芽,即加在庫(kù)的末尾授艰。"r"關(guān)鍵字可以有三個(gè)修飾符"a", "b"和"i"辨嗽。 "a"表示after,即將新成員加在指定成員之后淮腾。例如"ar -ra a.c liba.a b.c"表示將b.c加入liba.a并放在已有成員a.c之后糟需; "b"表示before,即將新成員加在指定成員之前谷朝。例如"ar -rb a.c liba.a b.c"洲押; "i"表示insert,跟"b"作用相同圆凰。
列出庫(kù)中已有成員
"ar -t liba.a"即可杈帐。如果加上"v"修飾符則會(huì)一并列出成員的日期等屬性。
刪除庫(kù)中成員
"ar -d liba.a a.c"表示從庫(kù)中刪除a.c成員专钉。如果庫(kù)中沒(méi)有這個(gè)成員ar也不會(huì)給出提示挑童。如果需要列出被刪除的成員或者成員不存在的信息,就加上"v"修飾符跃须。
從庫(kù)中解出成員
"ar -x liba.a b.c"
調(diào)整庫(kù)中成員的順序
使用"m"關(guān)鍵字站叼。與"r"關(guān)鍵字一樣,它也有3個(gè)修飾符"a","b", "i"菇民。如果要將b.c移動(dòng)到a.c之前尽楔,則使用"ar -mb a.c liba.a b.c"
使用用靜態(tài)庫(kù)
1 #include <iostream>
2 #include <string>
3 #include "unite_time.h"
4
5 int main() {
6 std::cout << "test static lib" << std::endl;
7 std::cout << get_time() << std::endl;
8 return 0;
9
10 }
Linux下使用靜態(tài)庫(kù),只需要在編譯的時(shí)候第练,指定靜態(tài)庫(kù)的搜索路徑(-L選項(xiàng))阔馋、指定靜態(tài)庫(kù)名(不需要lib前綴和.a后綴,-l選項(xiàng))娇掏。
g++ test.cpp -L./ -lunite_time -o test
就可以生成可執(zhí)行文件test啦呕寝,,我們執(zhí)行可執(zhí)行文件test驹碍,可以看到test 二進(jìn)制包大小是24k壁涎,成功生成了libunite_time.a
3.動(dòng)態(tài)庫(kù)
動(dòng)態(tài)庫(kù)
動(dòng)態(tài)庫(kù)凡恍,動(dòng)態(tài)庫(kù)是在程序運(yùn)行時(shí)被載入引用。 只在程序中做一個(gè)標(biāo)記怔球,當(dāng)用到被標(biāo)記的庫(kù)中的函數(shù)時(shí)嚼酝,程序會(huì)順著做的標(biāo)記找到庫(kù),然后調(diào)用需要的函數(shù)竟坛,并不會(huì)像靜態(tài)庫(kù)一樣將庫(kù)中的所有內(nèi)容都復(fù)制包含進(jìn)來(lái)闽巩。
為什么有了靜態(tài)庫(kù)還要設(shè)計(jì)動(dòng)態(tài)庫(kù)?
首先是空間浪費(fèi)是靜態(tài)庫(kù)的一個(gè)問(wèn)題担汤。我們可以這樣想假如某一個(gè)人靜態(tài)庫(kù)占用1M內(nèi)存涎跨,此時(shí)有2000個(gè)這樣的程序需要用到這個(gè)庫(kù),那么此時(shí)崭歧,每個(gè)程序都需要將這個(gè)靜態(tài)庫(kù)中的內(nèi)容拷貝一份到自己里面隅很,這樣內(nèi)存中就會(huì)產(chǎn)生多分庫(kù)函數(shù),并且這些會(huì)占用將近2GB的空間率碾。
另外一個(gè)問(wèn)題是由于靜態(tài)庫(kù)對(duì)程序的更新叔营、部署和發(fā)布帶來(lái)的麻煩。假如某一個(gè)靜態(tài)庫(kù)更新了所宰,所有使用它的應(yīng)用程序都需要重新編譯绒尊、發(fā)布給用戶(對(duì)于玩家來(lái)說(shuō),只是一個(gè)小小的改動(dòng)仔粥,但是卻會(huì)導(dǎo)致整個(gè)程序重新下載婴谱,全量更新)
因此我們需要引入動(dòng)態(tài)庫(kù),由于動(dòng)態(tài)庫(kù)是程序運(yùn)行時(shí)被載入的躯泰,不同的程序如果調(diào)用相同的庫(kù)谭羔,在內(nèi)存中里只需要有一份該庫(kù)的實(shí)例,避免了空間的浪費(fèi)斟冕,同時(shí)也解決了靜態(tài)庫(kù)對(duì)程序的更新口糕、部署和發(fā)布帶來(lái)的麻煩,用戶只需要更新動(dòng)態(tài)庫(kù)即可磕蛇,增量更新景描。
動(dòng)態(tài)庫(kù)特點(diǎn)總結(jié):
(1)動(dòng)態(tài)庫(kù)把對(duì)一些庫(kù)函數(shù)的鏈接載入推遲到程序運(yùn)行的時(shí)期⌒闫玻
(2)可以實(shí)現(xiàn)進(jìn)程之間的資源共享超棺。(因此動(dòng)態(tài)庫(kù)也稱為共享庫(kù))
(3)將一些程序升級(jí)變得簡(jiǎn)單。
(4)甚至可以真正做到鏈接載入完全由程序員在程序代碼中控制(顯式調(diào)用)呵燕。
## 創(chuàng)建動(dòng)態(tài)庫(kù)
1.linux動(dòng)態(tài)庫(kù)的命名規(guī)則
動(dòng)態(tài)鏈接庫(kù)的名字形式為 libxxx.so棠绘,前綴是 lib,后綴名為“.so”;針對(duì)于實(shí)際庫(kù)文件氧苍,每個(gè)共享庫(kù)都有個(gè)特殊的名字“so name”夜矗。在程序啟動(dòng)后,程序通過(guò)這個(gè)名字來(lái)告訴動(dòng)態(tài)加載器让虐,該載入哪個(gè)共享庫(kù)紊撕;在文件系統(tǒng)中,soname僅是一個(gè)鏈接到實(shí)際動(dòng)態(tài)庫(kù)的鏈接赡突。對(duì)于動(dòng)態(tài)庫(kù)而言对扶,每個(gè)庫(kù)實(shí)際上都有另一個(gè)名字給編譯器來(lái)用。它是一個(gè)指向?qū)嶋H庫(kù)鏡像文件的鏈接文件(lib+soname+.so)惭缰。
2.創(chuàng)建動(dòng)態(tài)庫(kù)(.so)
同樣是上面的代碼
第一步:生成位置無(wú)關(guān)的目標(biāo)文件.o(unite_time.o)浪南,此時(shí)要加編譯器選項(xiàng)-fpic
g++ -fPIC -c unite_time.cpp //-fPIC 創(chuàng)建與地址無(wú)關(guān)的編譯程序(pic,position independent code)漱受,是為了能夠在多個(gè)應(yīng)用程序間共享络凿。
第二步:生成動(dòng)態(tài)庫(kù),此時(shí)要加鏈接器選項(xiàng)-shared
g++ -shared -o libunite_time.so unite_time.o
也可以將上面兩步合為一步
g++ -fPIC -shared -o libunite_time.so unite_time.cpp
使用動(dòng)態(tài)庫(kù)
g++ test.cpp -L./ -lunite_time -o test
Linux系統(tǒng)在同一目錄下有可能找不到動(dòng)態(tài)庫(kù)位置拜效,那么在執(zhí)行程序時(shí)如何定位動(dòng)態(tài)庫(kù)的位置呢喷众?
(1) 當(dāng)系統(tǒng)加載可執(zhí)行代碼時(shí)候各谚,能夠知道其所依賴的庫(kù)的名字紧憾,但是還需要知道絕對(duì)路徑。此時(shí)就需要系統(tǒng)動(dòng)態(tài)載入器(dynamic linker/loader)昌渤。
(2)對(duì)于elf格式的可執(zhí)行程序赴穗,是由ld-linux.so*來(lái)完成的,它先后搜索elf文件的 DT_RPATH段—環(huán)境變LD_LIBRARY_PATH—/etc/ld.so.cache文件列表—/lib/,/usr/lib 目錄找到庫(kù)文件后將其載入內(nèi)存膀息。
如何讓系統(tǒng)能夠找到它般眉?
(1)如果安裝在/lib或者/usr/lib下,那么ld默認(rèn)能夠找到潜支,無(wú)需其它操作甸赃。
(2)如果安裝在其它目錄,需要將其添加到/etc/ld.so.cache文件中冗酿,步驟如下:
第一步:編輯/etc/ld.so.conf文件埠对,加入庫(kù)文件所在目錄的路徑
第二步:運(yùn)行l(wèi)dconfig ,該命令會(huì)重建/etc/ld.so.cache文件
4使用動(dòng)態(tài)庫(kù)運(yùn)行時(shí)加載的特性做熱更新
根據(jù)動(dòng)態(tài)庫(kù)運(yùn)行時(shí)加載的特性裁替,我們可以在不重新編譯可執(zhí)行文件的情況下项玛,對(duì)動(dòng)態(tài)庫(kù)進(jìn)行熱更新,使得test執(zhí)行過(guò)程中執(zhí)行熱熱加載
我們修改一下庫(kù)函數(shù)的內(nèi)容
/**
* @file: unite_time.cpp
* @author: mattbaisteins@gmail.com
* @date: 2020-08-03
* @brif:
**/
#include "unite_time.h"
#include <ctime>
std::string get_time() {
return "hello world";
}
重新生成動(dòng)態(tài)庫(kù)弱判,不重新編譯可執(zhí)行文件襟沮,可以發(fā)現(xiàn)結(jié)果已經(jīng)變成了新的動(dòng)態(tài)庫(kù)內(nèi)容