網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
前言
為什么用動(dòng)靜態(tài)庫(kù)
我們?cè)趯?shí)際開(kāi)發(fā)中,經(jīng)常要使用別人已經(jīng)實(shí)現(xiàn)好的功能,這是為了開(kāi)發(fā)效率和魯棒性(健壯性);因?yàn)槟切┕δ芏际琼敿獾墓こ處熞呀?jīng)寫(xiě)好的,并且已經(jīng)踐行多年的代碼。
那么如何使用他人開(kāi)發(fā)的功能呢?
1.庫(kù): 包括靜態(tài)庫(kù)與動(dòng)態(tài)庫(kù)。
2.開(kāi)源代碼。
3.基本的網(wǎng)絡(luò)功能調(diào)用,比如各種網(wǎng)絡(luò)接口、語(yǔ)音識(shí)別等等。
這其中,我們將詳細(xì)介紹靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù):
為什么在實(shí)際工作中一般將源碼打包成動(dòng)態(tài)靜態(tài)庫(kù)來(lái)給不同的人使用?
- 知識(shí)就是財(cái)富,你只是給某些人用一下你寫(xiě)的業(yè)務(wù)功能,并不想暴露源碼的邏輯,那就打包成.obj目標(biāo)文件甩給他用;
- 你也可以自己給自己封裝庫(kù),因?yàn)槟阕约壕幾g生成了.obj目標(biāo)文件,是已經(jīng)編譯完成了的,只需要把這個(gè)庫(kù)對(duì)應(yīng)的頭文件引入新的程序中,.obj文件放入庫(kù)路徑,這樣新的程序鏈接器就可以直接把功能鏈接起來(lái),方便部署使用,省去了反復(fù)編譯的麻煩;
動(dòng)態(tài)鏈接與靜態(tài)鏈接
一般情況下,為了更好的支持開(kāi)發(fā),第三方庫(kù)或者是語(yǔ)言庫(kù)都必須提供靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)(eg:C C++等官方庫(kù)),這是方便程序員根據(jù)需求功能進(jìn)行可執(zhí)行文件的生成;
動(dòng)態(tài)鏈接使用動(dòng)態(tài)庫(kù),而靜態(tài)鏈接使用靜態(tài)庫(kù)。
一般來(lái)說(shuō),我們gcc編譯默認(rèn)是動(dòng)態(tài)鏈接的而如果加上-static選項(xiàng),那么生成的可執(zhí)行文件將為靜態(tài)生成;
底層優(yōu)缺點(diǎn)
動(dòng)態(tài)鏈接文件信息:
靜態(tài)鏈接文件信息:
可以明顯發(fā)現(xiàn)動(dòng)態(tài)鏈接的文件大小明顯要比靜態(tài)鏈接的文件大小要小多了
這是因?yàn)?strong>動(dòng)態(tài)鏈接是當(dāng)程序執(zhí)行到調(diào)用接口時(shí),編譯器再去特定路徑查找目標(biāo)接口的可執(zhí)行文件,直接進(jìn)行計(jì)算;
靜態(tài)鏈接比較暴力,鏈接時(shí)候直接將目標(biāo)接口的二進(jìn)制代碼全部鏈接到原文件中去,這也就是靜態(tài)鏈接生成的文件這么大的原因了;(畢竟把二進(jìn)制代碼copy過(guò)來(lái)了)
但是這些都是相對(duì)的,有優(yōu)點(diǎn)就有缺點(diǎn):
萬(wàn)一動(dòng)態(tài)庫(kù)路徑中的庫(kù)丟失損壞 ,動(dòng)態(tài)鏈接的程序到目標(biāo)位置了,過(guò)來(lái)用的時(shí)候肯定出錯(cuò)了;
靜態(tài)鏈接因?yàn)榫幾g的時(shí)候吧二進(jìn)制代碼考過(guò)去了,不依賴(lài)原生庫(kù),即便原庫(kù)代碼丟失也沒(méi)事;
小結(jié)
Linux下的動(dòng)靜態(tài)庫(kù)
linux下庫(kù)的命名格式一般為:
靜態(tài)庫(kù): lib+庫(kù)的名字+.a eg:c標(biāo)準(zhǔn)庫(kù)為 libc.a
動(dòng)態(tài)庫(kù): lib+庫(kù)的名字+.so
靜態(tài)庫(kù)是指程序在編譯鏈接的時(shí)候把庫(kù)的二進(jìn)制可執(zhí)行代碼鏈接到可執(zhí)行文件中。程序運(yùn)行的時(shí)候?qū)⒉辉傩枰o態(tài)庫(kù)。
而動(dòng)態(tài)庫(kù)則是指程序在運(yùn)行的時(shí)候才去啟動(dòng)指定位置的動(dòng)態(tài)庫(kù)的代碼,使其加載到內(nèi)存共享區(qū)中,多個(gè)程序共享使用庫(kù)的二進(jìn)制代碼, 不用拉到本文件中來(lái)。
- 一個(gè)與動(dòng)態(tài)庫(kù)鏈接的可執(zhí)行文件僅僅包含它用到的函數(shù)入口地址的一個(gè)表(頭文件),而不是外部函數(shù)所在目標(biāo)文件(.o)的整個(gè)機(jī)器碼
- 在可執(zhí)行文件開(kāi)始運(yùn)行以前,外部函數(shù)的機(jī)器碼由操作系統(tǒng)從磁盤(pán)上的該動(dòng)態(tài)庫(kù)中復(fù)制到內(nèi)存中,這個(gè)過(guò)程稱(chēng)為動(dòng)態(tài)鏈接(dynamic linking),也就是說(shuō),動(dòng)態(tài)鏈接是在需要調(diào)用接口時(shí)才會(huì)去將所用接口的二進(jìn)制代碼拷貝到內(nèi)存中。
- 當(dāng)一個(gè)庫(kù)多文件使用時(shí),動(dòng)態(tài)庫(kù)只有一份,所以可以在多個(gè)程序間共享,所以動(dòng)態(tài)鏈接使得可執(zhí)行文件更小,節(jié)省了磁盤(pán)空間。–>操作系統(tǒng)采用虛擬內(nèi)存機(jī)制允許物理內(nèi)存中的一份動(dòng)態(tài)庫(kù)被要用到該庫(kù)的所有進(jìn)程共用,節(jié)省了內(nèi)存和磁盤(pán)空間。
- 這里需要提一下的是,我們之前所提過(guò)的進(jìn)程地址空間中有一個(gè)共享區(qū),而一般動(dòng)態(tài)庫(kù)的代碼就映射在共享區(qū),所有進(jìn)程都共享著動(dòng)態(tài)庫(kù)的代碼。
動(dòng)靜態(tài)庫(kù)的對(duì)比
動(dòng)態(tài)庫(kù)被加載在內(nèi)存中,可以供多個(gè)使用庫(kù)的程序共享映射到自己的虛擬地址空間使用,因此可以減少頁(yè)面交換以及降低內(nèi)存中代碼冗余,并且因?yàn)榕c源程序模塊分離,因此開(kāi)發(fā)模式比較好。
而加載動(dòng)態(tài)庫(kù)的程序運(yùn)行速度相對(duì)較慢,因?yàn)閯?dòng)態(tài)庫(kù)運(yùn)行時(shí)加載,映射到虛擬地址空間后需要重新根據(jù)映射起始地址計(jì)算函數(shù)/變量地址。
靜態(tài)庫(kù)直接把二進(jìn)制代碼鏈接過(guò)來(lái),與動(dòng)態(tài)庫(kù)的使用恰好相反,其運(yùn)行速度相對(duì)較快,但消耗資源較多。
打包靜態(tài)庫(kù)
庫(kù)函數(shù)源文件:
//file1: Add.c #include<stdio.h> int Add(int a,int b) { return a+b; } //file2: Sub.c #include<stdio.h> int Sub(int a,int b) { return a-b; }
生成靜態(tài)庫(kù)需要先生成目標(biāo)文件(.o)再進(jìn)行打包,故先編寫(xiě)相應(yīng)的源文件再將其編譯成目標(biāo)文件:
//生成兩個(gè)二進(jìn)制目標(biāo)文件 [root@VM-8-15-centos fighting]# gcc -c Sub.c -o Sub.o [root@VM-8-15-centos fighting]# gcc -c Add.c -o Add.o
此時(shí)的add.o和sub.o文件是已經(jīng)編譯好但還沒(méi)有鏈接的兩個(gè)文件;
此時(shí)再用 ar命令,歸檔工具將其打包成靜態(tài)庫(kù):
//將這倆二進(jìn)制目標(biāo)文件打包成靜態(tài)庫(kù) [lyl@VM-4-3-centos 2022-3-14]$ ar -rc libmycal.a Add.o Sub.o //`rc`表示(replace and create)
查看靜態(tài)庫(kù)
//查看靜態(tài)庫(kù)的目錄列表 [root@VM-8-15-centos fighting]# ar -tv libmycal.a `tv`表示(列出靜態(tài)庫(kù)中文件 and verbose詳細(xì)信息) rw-r--r-- 0/0 936 Jan 24 16:57 2023 Add.o rw-r--r-- 0/0 1240 Jan 24 16:57 2023 Sub.o
使用靜態(tài)庫(kù)
將庫(kù)的頭文件和靜態(tài)庫(kù)都放到指定lib目錄下:
調(diào)用我們的庫(kù)接口代碼:
#include <stdio.h> #include "add.h" #include "sub.h" int main() { int a = 10; int b = 20; printf("a+b:%d\n", Add(a, b)); printf("a-b:%d\n", Sub(a, b)); return 0; }
編譯:
發(fā)現(xiàn)報(bào)錯(cuò): 這是因?yàn)間cc編譯時(shí)去鏈接庫(kù)和頭文件,是去默認(rèn)路徑以及當(dāng)前源文件路徑下尋找;
//gcc 尋找的默認(rèn)頭文件路徑:不建議污染原生庫(kù) /usr/include //gcc 尋找的默認(rèn)庫(kù)文件路徑: /lib , lib64 ......等
而我們將靜態(tài)庫(kù)打包到lib目錄下,gcc編譯時(shí)就找不到我們的庫(kù)了,因此我們編譯的時(shí)候,需要指定一些選項(xiàng),并且?guī)下窂?/lib;
因此,正確鏈接的指令為:
gcc -o test test.c -I ./lib -L ./lib -lmycal -static
- -I(大寫(xiě)i) + 指定路徑:告知gcc除了默認(rèn)路徑之外,還要去尋找這個(gè)指定路徑的頭文件。
- -L + 指定路徑:除默認(rèn)庫(kù)路徑以外,需要尋找這個(gè)指定路徑的庫(kù)
- -l(小寫(xiě)L)+ 庫(kù)名稱(chēng):表示要具體鏈接的是哪一個(gè)庫(kù);(因?yàn)槁窂较聨?kù)可能不止一個(gè))
- -static 選擇使用靜態(tài)庫(kù)的靜態(tài)鏈接
由此,我們就靜態(tài)鏈接生成了一個(gè)可執(zhí)行文件test,運(yùn)行test程序結(jié)果如下:
此時(shí)我們刪除靜態(tài)庫(kù),發(fā)現(xiàn)照樣可以運(yùn)行,因?yàn)殪o態(tài)庫(kù)中Add和Sub的二進(jìn)制代碼已經(jīng)被鏈接入test程序中了,不怕原生庫(kù)沒(méi)了!
可見(jiàn),有時(shí)候編譯選項(xiàng)多而雜,難記,特別是文件一多,寫(xiě)的很麻煩,介紹一個(gè)camke構(gòu)建項(xiàng)目的工具,C/C++開(kāi)發(fā)人員必會(huì)技能;文章鏈接
打包動(dòng)態(tài)庫(kù)
類(lèi)似與打包靜態(tài)庫(kù)使用的ar歸檔工具,動(dòng)態(tài)庫(kù)也有自己的語(yǔ)法,我們將生成動(dòng)態(tài)庫(kù)的依賴(lài)關(guān)系及方法寫(xiě)進(jìn)自動(dòng)化構(gòu)建工具(Makefile)中::
顯然手動(dòng)寫(xiě)Makefile和上面手動(dòng)打包靜態(tài)庫(kù)一樣,麻煩很多,我的評(píng)價(jià)是,直接cmake起飛;
注意:
- 由于動(dòng)態(tài)庫(kù)在內(nèi)存中是可加載的,它可能在內(nèi)存中的任意位置,也可能被映射到進(jìn)程地址空間的每個(gè)區(qū)域,所以為了保證庫(kù)當(dāng)中的代碼執(zhí)行不會(huì)出錯(cuò),也就是要保證庫(kù)中的代碼是與位置無(wú)關(guān)的,因此生成.o文件時(shí)需要帶上-fPIC選項(xiàng)表示生成與位置無(wú)關(guān)碼。
- 這里由于在依賴(lài)關(guān)系中已經(jīng)點(diǎn)明了要生成的目標(biāo)文件,故不帶上$@也可以
- 打包動(dòng)態(tài)庫(kù)不是像靜態(tài)庫(kù)一樣先gcc -o再使用ar(歸檔工具);
- 而是用gcc 帶上-shared選項(xiàng)表示生成共享動(dòng)態(tài)庫(kù)格式,這也體現(xiàn)了動(dòng)態(tài)庫(kù)代碼映射在共享區(qū)的特點(diǎn)
編寫(xiě)好Makefile之后 make指令構(gòu)建動(dòng)態(tài)庫(kù) libmycal.so:
使用動(dòng)態(tài)庫(kù)
和靜態(tài)庫(kù)一樣,我們把頭文件和.so庫(kù)文件放入lib目錄,gcc的時(shí)候帶上選項(xiàng);
gcc -o test test.c -I ./lib -L ./lib -l mycal //因?yàn)槭莿?dòng)態(tài)鏈接 所以不用帶-static了
然后編譯過(guò)了,運(yùn)行程序時(shí)發(fā)現(xiàn)有問(wèn)題,打不開(kāi)動(dòng)態(tài)庫(kù)?:
既然編譯都聲稱(chēng)可執(zhí)行程序了,此時(shí)的可執(zhí)行程序是沒(méi)問(wèn)題的,因此已經(jīng)與編譯過(guò)程無(wú)關(guān)了;
那么這屬于運(yùn)行問(wèn)題,其實(shí)運(yùn)行時(shí)系統(tǒng)也會(huì)去默認(rèn)路徑下找到我們所使用的動(dòng)態(tài)庫(kù),但在默認(rèn)路徑下沒(méi)有我們的庫(kù)。
這里解決方法有多種,但我傾向于推薦下面這一種:
修改環(huán)境變量LD_LIBRARY_PATH
,將動(dòng)態(tài)庫(kù)所在路徑.lib添加到該環(huán)境變量中,這樣程序在運(yùn)行時(shí)系統(tǒng)就能夠找到動(dòng)態(tài)庫(kù),從而運(yùn)行成功。
當(dāng)然,還可以拷貝我們的.so文件到系統(tǒng)共享庫(kù)路徑下, 一般指/usr/lib;
但是這可能會(huì)污染系統(tǒng)原生的庫(kù),一般不推薦這樣做。
還有一種方法,在我們的系統(tǒng)下有**/etc/ld.so.conf.d/**這個(gè)路徑:
我們可以在這個(gè)路徑下制造自己的.conf,然后再將自己的庫(kù)路徑寫(xiě)進(jìn)這個(gè)conf中;
但是還是有點(diǎn)污染了原生庫(kù),不建議;
小結(jié)
linux打包使用靜態(tài)庫(kù):
接口的.c源文件–>.o目標(biāo)二進(jìn)制文件–>ar rc(歸檔工具)進(jìn)行打包成.a靜態(tài)庫(kù),編譯程序使用時(shí)帶上-static;(注意帶上頭文件尋找路徑選項(xiàng))
linux打包使用動(dòng)態(tài)庫(kù):
接口的.c源文件–>.o目標(biāo)二進(jìn)制文件(需帶上-fPIC 與位置無(wú)關(guān))–>-shard 打包動(dòng)態(tài)庫(kù);(注意帶上頭文件尋找路徑選項(xiàng))+(注意添加動(dòng)態(tài)庫(kù)尋找路徑)
win下打包動(dòng)靜態(tài)庫(kù)
比如通過(guò)VS2019打包,由于是可視化界面方便操作,不再贅述,參考下方文章;
參考文章
總結(jié)
原文鏈接:https://blog.csdn.net/wtl666_6/article/details/128757570
相關(guān)推薦
- 2024-03-07 MyBatis多表映射
- 2022-06-02 詳解IIS在A(yíng)SP.NET?Core下的兩種部署模式_實(shí)用技巧
- 2023-05-29 python怎樣判斷一個(gè)數(shù)值(字符串)為整數(shù)_python
- 2022-09-19 Python使用read_csv讀數(shù)據(jù)遇到分隔符問(wèn)題的2種解決方式_python
- 2022-04-15 python實(shí)現(xiàn)AES算法及AES-CFB8加解密源碼_python
- 2022-05-31 Python學(xué)習(xí)之加密模塊使用詳解_python
- 2022-03-20 Oracle進(jìn)階DECODE函數(shù)使用詳解_oracle
- 2022-08-13 解決Artifact spbjh:war exploded: Error during artifa
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過(guò)濾器
- Spring Security概述快速入門(mén)
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支