網站首頁 編程語言 正文
前言
為什么用動靜態庫
我們在實際開發中,經常要使用別人已經實現好的功能,這是為了開發效率和魯棒性(健壯性);因為那些功能都是頂尖的工程師已經寫好的,并且已經踐行多年的代碼。
那么如何使用他人開發的功能呢?
1.庫: 包括靜態庫與動態庫。
2.開源代碼。
3.基本的網絡功能調用,比如各種網絡接口、語音識別等等。
這其中,我們將詳細介紹靜態庫和動態庫:
為什么在實際工作中一般將源碼打包成動態靜態庫來給不同的人使用?
- 知識就是財富,你只是給某些人用一下你寫的業務功能,并不想暴露源碼的邏輯,那就打包成.obj目標文件甩給他用;
- 你也可以自己給自己封裝庫,因為你自己編譯生成了.obj目標文件,是已經編譯完成了的,只需要把這個庫對應的頭文件引入新的程序中,.obj文件放入庫路徑,這樣新的程序鏈接器就可以直接把功能鏈接起來,方便部署使用,省去了反復編譯的麻煩;
動態鏈接與靜態鏈接
一般情況下,為了更好的支持開發,第三方庫或者是語言庫都必須提供靜態庫和動態庫(eg:C C++等官方庫),這是方便程序員根據需求功能進行可執行文件的生成;
動態鏈接使用動態庫,而靜態鏈接使用靜態庫。
一般來說,我們gcc編譯默認是動態鏈接的而如果加上-static選項,那么生成的可執行文件將為靜態生成;
底層優缺點
動態鏈接文件信息:
靜態鏈接文件信息:
可以明顯發現動態鏈接的文件大小明顯要比靜態鏈接的文件大小要小多了
這是因為動態鏈接是當程序執行到調用接口時,編譯器再去特定路徑查找目標接口的可執行文件,直接進行計算;
靜態鏈接比較暴力,鏈接時候直接將目標接口的二進制代碼全部鏈接到原文件中去,這也就是靜態鏈接生成的文件這么大的原因了;(畢竟把二進制代碼copy過來了)
但是這些都是相對的,有優點就有缺點:
萬一動態庫路徑中的庫丟失損壞 ,動態鏈接的程序到目標位置了,過來用的時候肯定出錯了;
靜態鏈接因為編譯的時候吧二進制代碼考過去了,不依賴原生庫,即便原庫代碼丟失也沒事;
小結
Linux下的動靜態庫
linux下庫的命名格式一般為:
靜態庫: lib+庫的名字+.a eg:c標準庫為 libc.a
動態庫: lib+庫的名字+.so
靜態庫是指程序在編譯鏈接的時候把庫的二進制可執行代碼鏈接到可執行文件中。程序運行的時候將不再需要靜態庫。
而動態庫則是指程序在運行的時候才去啟動指定位置的動態庫的代碼,使其加載到內存共享區中,多個程序共享使用庫的二進制代碼, 不用拉到本文件中來。
- 一個與動態庫鏈接的可執行文件僅僅包含它用到的函數入口地址的一個表(頭文件),而不是外部函數所在目標文件(.o)的整個機器碼
- 在可執行文件開始運行以前,外部函數的機器碼由操作系統從磁盤上的該動態庫中復制到內存中,這個過程稱為動態鏈接(dynamic linking),也就是說,動態鏈接是在需要調用接口時才會去將所用接口的二進制代碼拷貝到內存中。
- 當一個庫多文件使用時,動態庫只有一份,所以可以在多個程序間共享,所以動態鏈接使得可執行文件更小,節省了磁盤空間。–>操作系統采用虛擬內存機制允許物理內存中的一份動態庫被要用到該庫的所有進程共用,節省了內存和磁盤空間。
- 這里需要提一下的是,我們之前所提過的進程地址空間中有一個共享區,而一般動態庫的代碼就映射在共享區,所有進程都共享著動態庫的代碼。
動靜態庫的對比
動態庫被加載在內存中,可以供多個使用庫的程序共享映射到自己的虛擬地址空間使用,因此可以減少頁面交換以及降低內存中代碼冗余,并且因為與源程序模塊分離,因此開發模式比較好。
而加載動態庫的程序運行速度相對較慢,因為動態庫運行時加載,映射到虛擬地址空間后需要重新根據映射起始地址計算函數/變量地址。
靜態庫直接把二進制代碼鏈接過來,與動態庫的使用恰好相反,其運行速度相對較快,但消耗資源較多。
打包靜態庫
庫函數源文件:
//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; }
生成靜態庫需要先生成目標文件(.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
此時的add.o和sub.o文件是已經編譯好但還沒有鏈接的兩個文件;
此時再用 ar命令,歸檔工具將其打包成靜態庫:
//將這倆二進制目標文件打包成靜態庫 [lyl@VM-4-3-centos 2022-3-14]$ ar -rc libmycal.a Add.o Sub.o //`rc`表示(replace and create)
查看靜態庫
//查看靜態庫的目錄列表 [root@VM-8-15-centos fighting]# ar -tv libmycal.a `tv`表示(列出靜態庫中文件 and verbose詳細信息) 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
使用靜態庫
將庫的頭文件和靜態庫都放到指定lib目錄下:
調用我們的庫接口代碼:
#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; }
編譯:
發現報錯: 這是因為gcc編譯時去鏈接庫和頭文件,是去默認路徑以及當前源文件路徑下尋找;
//gcc 尋找的默認頭文件路徑:不建議污染原生庫 /usr/include //gcc 尋找的默認庫文件路徑: /lib , lib64 ......等
而我們將靜態庫打包到lib目錄下,gcc編譯時就找不到我們的庫了,因此我們編譯的時候,需要指定一些選項,并且帶上路徑./lib;
因此,正確鏈接的指令為:
gcc -o test test.c -I ./lib -L ./lib -lmycal -static
- -I(大寫i) + 指定路徑:告知gcc除了默認路徑之外,還要去尋找這個指定路徑的頭文件。
- -L + 指定路徑:除默認庫路徑以外,需要尋找這個指定路徑的庫
- -l(小寫L)+ 庫名稱:表示要具體鏈接的是哪一個庫;(因為路徑下庫可能不止一個)
- -static 選擇使用靜態庫的靜態鏈接
由此,我們就靜態鏈接生成了一個可執行文件test,運行test程序結果如下:
此時我們刪除靜態庫,發現照樣可以運行,因為靜態庫中Add和Sub的二進制代碼已經被鏈接入test程序中了,不怕原生庫沒了!
可見,有時候編譯選項多而雜,難記,特別是文件一多,寫的很麻煩,介紹一個camke構建項目的工具,C/C++開發人員必會技能;文章鏈接
打包動態庫
類似與打包靜態庫使用的ar歸檔工具,動態庫也有自己的語法,我們將生成動態庫的依賴關系及方法寫進自動化構建工具(Makefile)中::
顯然手動寫Makefile和上面手動打包靜態庫一樣,麻煩很多,我的評價是,直接cmake起飛;
注意:
- 由于動態庫在內存中是可加載的,它可能在內存中的任意位置,也可能被映射到進程地址空間的每個區域,所以為了保證庫當中的代碼執行不會出錯,也就是要保證庫中的代碼是與位置無關的,因此生成.o文件時需要帶上-fPIC選項表示生成與位置無關碼。
- 這里由于在依賴關系中已經點明了要生成的目標文件,故不帶上$@也可以
- 打包動態庫不是像靜態庫一樣先gcc -o再使用ar(歸檔工具);
- 而是用gcc 帶上-shared選項表示生成共享動態庫格式,這也體現了動態庫代碼映射在共享區的特點
編寫好Makefile之后 make指令構建動態庫 libmycal.so:
使用動態庫
和靜態庫一樣,我們把頭文件和.so庫文件放入lib目錄,gcc的時候帶上選項;
gcc -o test test.c -I ./lib -L ./lib -l mycal //因為是動態鏈接 所以不用帶-static了
然后編譯過了,運行程序時發現有問題,打不開動態庫?:
既然編譯都聲稱可執行程序了,此時的可執行程序是沒問題的,因此已經與編譯過程無關了;
那么這屬于運行問題,其實運行時系統也會去默認路徑下找到我們所使用的動態庫,但在默認路徑下沒有我們的庫。
這里解決方法有多種,但我傾向于推薦下面這一種:
修改環境變量LD_LIBRARY_PATH
,將動態庫所在路徑.lib添加到該環境變量中,這樣程序在運行時系統就能夠找到動態庫,從而運行成功。
當然,還可以拷貝我們的.so文件到系統共享庫路徑下, 一般指/usr/lib;
但是這可能會污染系統原生的庫,一般不推薦這樣做。
還有一種方法,在我們的系統下有**/etc/ld.so.conf.d/**這個路徑:
我們可以在這個路徑下制造自己的.conf,然后再將自己的庫路徑寫進這個conf中;
但是還是有點污染了原生庫,不建議;
小結
linux打包使用靜態庫:
接口的.c源文件–>.o目標二進制文件–>ar rc(歸檔工具)進行打包成.a靜態庫,編譯程序使用時帶上-static;(注意帶上頭文件尋找路徑選項)
linux打包使用動態庫:
接口的.c源文件–>.o目標二進制文件(需帶上-fPIC 與位置無關)–>-shard 打包動態庫;(注意帶上頭文件尋找路徑選項)+(注意添加動態庫尋找路徑)
win下打包動靜態庫
比如通過VS2019打包,由于是可視化界面方便操作,不再贅述,參考下方文章;
參考文章
總結
原文鏈接:https://blog.csdn.net/wtl666_6/article/details/128757570
相關推薦
- 2022-05-18 C語言模擬內存函數分析之mencpy與memmove_C 語言
- 2022-05-10 怎樣理解單項數據流
- 2022-05-15 Docker基本概念和底層原理解析_docker
- 2021-12-06 c#二叉樹存儲介紹_C#教程
- 2021-12-07 C語言實現頁面置換算法(FIFO、LRU)_C 語言
- 2023-04-02 深入分析Golang?Server源碼實現過程_Golang
- 2023-01-17 使用matplotlib繪制熱圖(heatmap)全過程_python
- 2022-03-19 C語言常量介紹_C 語言
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支