Linux中的靜態庫和動態庫

首先 我們要明白什么是庫?

庫(Library)是一組預編譯的代碼,提供特定的功能,可以被多個程序共享調用,避免重復編寫代碼。在鏈接步驟中,鏈接器將從庫文件取得所需的代碼,復制到生成的可執行文件中

Linux 中常見的庫文件有兩種,一種.a為后綴,為靜態庫,另一種以.so為后綴,為動態庫。
靜態庫:在程序編譯時,將庫的代碼“拷貝”到最終的可執行文件中;
動態庫(共享庫):在程序運行時,動態載入庫的代碼,多個程序可以共享同一份庫文件。

靜態庫

靜態庫通常以.a為后綴(在Linux下),是多個目標文件(.o)組成的歸檔文件(Archive),里面存儲著一組目標文件。

特點

  • 在鏈接(Linking)的時候被合并到可執行文件中
  • 可執行文件較大,因為庫的代碼已經復制到程序中
  • 加載快,無需在運行時再載入庫文件
  • 無需在運行環境中存在庫文件,便于部署

作用

  • 方便將多個目標文件打包成一個完整的可執行程序
  • 使用簡單,不依賴外部共享庫

創建靜態庫

# 先編譯源文件生成目標文件
gcc -c file1.c -o file1.o
gcc -c file2.c -o file2.o# 將目標文件打包為靜態庫
ar rcs libmylib.a file1.o file2.o# 編譯時鏈接靜態庫
gcc main.o -L. -lmylib -o myprogram

使用靜態庫

  • 在編譯時指定庫路徑和庫名(-L和-l參數)
    • l:指定庫名(庫的文件名為 libxxx.a,庫名為 xxx)
    • L:指定庫路徑
    • static:使用靜態鏈接
gcc -static main.c -o main -l math -L ./
// 錯誤:gcc -l math -static main.c -o main -L ./

特別注意,必須把 -l math 放在后面。放在最后時它是這樣的一個解析過程:

  • 鏈接器從左往右掃描可重定位目標文件和靜態庫
  • 掃描 main.c 時,發現兩個未解析的符號 add 和 sub,記住這兩個未解析的符號
  • 掃描 libmath.a,找到了前面未解析的符號,因此提取相關代碼
  • 最終沒有任何未解析的符號,編譯鏈接完成
    那如果將 -l math 放在前面,又是怎樣的情況呢?
  • 鏈接器從左往右掃描可重定位目標文件和靜態庫
  • 掃描 libmath.a,由于前面沒有任何未解析的符號,因此不會提取任何代碼
  • 掃描 main.c,發現未解析的符號 add 和 sub
  • 掃描結束,還有兩個未解析的符號,因此編譯鏈接報錯
    如果把-l math放在前面,編譯結果如下:

在這里插入圖片描述

我們看看最終生成的文件大小:
在這里插入圖片描述
生成可執行文件大小為892k
由于最終生成的可執行文件中已經包含了add和sub相關的二進制代碼,因此這個可執行文件在一個沒有libmath.a的Linux系統中也能正常運行。

動態庫

動態庫和靜態庫類似,但是它并不在鏈接時將需要的二進制代碼都“拷貝”到可執行文件中,而是僅僅“拷貝”一些重定位和符號表信息,這些信息可以在程序運行時完成真正的鏈接過程。Linux 中這類庫的名字一般是 libxxx.so。(shared object)

符號表

  • 符號表記錄了函數和變量的名字、地址等信息,是連接器、加載器用來解決引用關系的重要數據結構。
  • 在動態庫中,符號表包括:
    • 導出符號:庫中提供給外部調用的函數和變量
    • 需要被其他程序或庫引用的符號

重定位信息

  • 重定位是指在程序或庫加載到內存后,動態調整代碼中的地址引用,使得符號指向正確的內存地址。
  • 重定位信息記錄了哪些位置需要修正(如函數調用、全局變量訪問位置)
  • 在靜態鏈接中,重定位在鏈接階段完成;在動態鏈接中,加載時由系統完成。

定義:

  • 動態庫也稱“共享庫”,以.so(Shared Object)為后綴
  • 在程序運行時動態載入,多個程序可以共享同一份庫文件

特點

  • 在程序加載時才載入庫(動態鏈接)
  • 可減小可執行文件大小,因為庫在運行時加載
  • 便于維護和升級,只需替換庫文件,無需重新編譯所有程序
  • 運行時依賴,程序必須找到對應的.so文件

作用

  • 方便庫的共享和維護
  • 可以實現插件式開發或模塊化設計

創建動態庫

# 編譯生成共享庫
gcc -fPIC -c file1.c -o file1.o
gcc -fPIC -c file2.c -o file2.o
gcc -shared -o libmylib.so file1.o file2.o# 編譯程序鏈接動態庫
gcc main.o -L. -lmylib -o myprogram

注意:

  • -fPIC:生成位置無關代碼(Position Independent Code)
  • -shared:生成共享庫

使用動態庫

步驟命令示例說明
1. 創建共享庫gcc -fPIC -c mylib.c -o mylib.o
gcc -shared -o libmylib.so mylib.o
生成.so文件
2. 編譯調用程序gcc main.c -L. -lmylib連接到共享庫
3. 設置環境變量或放庫到系統路徑export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
sudo cp libmylib.so /usr/lib/
讓運行時找到共享庫
4. 運行程序./myprogram運行程序,調用共享庫中的函數

找不到的原因

原因類別描述解決措施
庫路徑未在標準路徑或未指定庫文件所在路徑未加入系統路徑或未在運行時指定設置LD_LIBRARY_PATHrpath
缺少ldconfig刷新緩存添加新庫后未刷新系統緩存運行ldconfig
編譯參數不正確編譯時未設置-rpath或路徑錯誤使用-Wl,-rpath指定路徑
依賴缺失庫依賴的其他庫不存在或路徑錯誤ldd檢測,安裝缺失的庫
權限不足庫文件權限限制修改文件權限

主要原因是系統在運行時無法定位到庫文件的存放路徑,可能因為庫路徑沒有配置正確、庫文件放錯位置、未刷新緩存或依賴缺失等。

當在運行程序時,動態庫(.so文件)找不到,系統會報錯,導致程序無法正常啟動。這種情況常見的錯誤信息有:

error while loading shared libraries: libmylib.so: cannot open shared object file: No such file or directory

動態庫找不到時的解決辦法

1.臨時設置環境變量LD_LIBRARY_PATH

這是一種快速方便的方式,臨時告訴系統去哪里找共享庫。

export LD_LIBRARY_PATH=.:/path/to/lib:$LD_LIBRARY_PATH
./your_program

示例:假設libmylib.so在當前目錄:

export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
./your_program

缺點:此設置只在當前終端有效,關閉終端后失效。

2.將共享庫拷貝到系統標準目錄

將你的共享庫復制到系統的庫目錄(如/usr/lib或/usr/local/lib),然后刷新緩存。

sudo cp libmylib.so /usr/lib/
sudo ldconfig

說明

ldconfig命令會刷新系統的庫緩存,讓系統知道新加入的庫。

注意:需要管理員權限。

3.修改程序的RPATH或RUNPATH(在編譯時指定)

在編譯時,將庫的路徑提前嵌入到可執行文件中,使其在運行時自動查找。

例:

gcc main.c -L/path/to/lib -Wl,-rpath=/path/to/lib -lmylib -o myprogram
  • -Wl,-rpath=路徑:在可執行文件中指定庫搜索路徑

執行后,無需設置LD_LIBRARY_PATH,系統會自動在指定路徑查找庫。

4.使用LD_LIBRARY_PATH環境變量文件(配置全局)

編輯/etc/ld.so.conf或在/etc/ld.so.conf.d/目錄中添加自定義路徑,然后運行

sudo ldconfig

這樣,系統會在指定路徑中查找庫。

5. 動態調試:使用ldd命令檢測缺失的依賴庫

運行:

ldd ./your_program

可以顯示程序依賴的所有共享庫,查看是否有“not found”的庫。

如果發現缺失的庫,可以用上面的方法解決。

小結

方法作用適用場景
LD_LIBRARY_PATH臨時指定庫路徑調試或快速測試
拷貝到系統目錄將庫放到標準路徑,系統自動加載部署時,保證程序可以找到庫
RPATH/RUNPATH在編譯時嵌入路徑內部控制,避免環境變量設置
ldconfig配置全系統范圍配置庫搜索路徑長期維護或系統級別的配置
ldd命令檢測依賴查看依賴關系,找出缺失的庫調試、排錯

靜態庫與動態庫的區別

特性靜態庫(.a動態庫(.so
鏈接時間編譯時(靜態鏈接)運行時(動態鏈接)
生成文件大小較大(所用到代碼要從二進制文件中拷貝一份,復制到可執行文件中)較小(庫文件單獨存在,僅僅復制了一些重定位和符號表信息)
占用空間運行時每個程序都持有一份庫的副本多個程序共享同一份庫(內存節省)
升級維護需要重新編譯程序只需替換庫文件,無需重新編譯
依賴關系不需要庫文件存在于運行環境中需要庫文件存在于運行環境中
運行速度略快(沒有動態加載耗時)因為需要動態加載,可能略慢

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/88158.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/88158.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/88158.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Vue3-組件化-Vue核心思想之一

一.組件及組件化1.組件化的作用由于之前的代碼全寫在一個App.vue這個文件里面,會到導致一個文件代碼過于多而且不易復用,所以有組件化的思想。2.組件的使用①創建創建一個.vue文件,使用setup的簡寫方式會自動導出.vue文件②導入import 組件對…

OS學習筆記

《幾個基本知識點》 一、2的冪 1024210 51229 25628 12827 6426 3225 1624 823 422 221 K210 G220 M230 T240 P250 E260 Z270 Y280 R290 Q2100 二、常用的ASCII碼 ‘1’0x31 ‘A’0x41 ‘a’0x61 空格0x20 換行0x0A 回車0x0D 三、存儲器層次中的典型速度 CPU/寄存器&#xff1a…

嵌入式學習筆記-MCU階段-DAY01

恭喜大家完成了C語言的學習,現在咱們來到咱們的硬件MCU階段,咱們這里的工程用的是keil,環境搭建不再贅述,希望大家在這一階段仍然學的愉快 1.資料部分 用的最多的就是STM32f103的手冊,搭配STM32F103ZET6的開發板 2.概…

three案例 Three.js波紋效果演示

波紋效果,在智慧城市可視化開發中經常用到,這里分享一個比較好玩的案例 以下是詳細的步驟: 初始化部分:設置 Three.js 環境,包括場景、相機、渲染器和控制器 幾何體和紋理:創建平面幾何體并加載波紋紋理 著…

Flutter-詳解布局

上一章我們詳細的學習了 Flutter 中的Widget,這一章我們將要學習 Flutter 的布局, 在上一章我們了解到了:Everything is a widget,在 Flutter 中幾乎所有的對象都是一個 Widget ,當然也包括布局,Flutter 的…

EPLAN 電氣制圖:建立自己的部件庫,添加部件-加SQL Server安裝教程(三)上

在智能電氣設計領域,EPLAN 作為主流的設計軟件,其部件庫的完善程度直接影響項目設計的效率與質量。本文將從實際操作出發,詳細講解如何在 EPLAN 中建立專屬部件庫并添加部件,為電氣設計奠定堅實基礎。一、部件庫:電氣設…

靜態路由進階實戰全解

一、項目背景二、項目拓撲圖三、設備命名與IP地址規劃設備名接口編號IP地址規劃R1GE0/0192.168.1.1/24GE0/1172.16.1.1/24R2GE0/0192.168.1.2/24GE0/1192.168.2.2/24R3GE0/0192.168.2.3/24GE0/1192.168.3.3/24GE0/2192.168.4.3/24R4GE0/0192.168.3.4/24GE0/1192.168.4.4/24GE0/…

stm32hal模塊驅動(3)ssd1305 oled驅動

SD1305 OLED 驅動芯片詳細介紹SSD1305 是 Solomon Systech 公司生產的一款 OLED 顯示控制器/驅動器,專為 128x64 或 128x32 點陣的 OLED 顯示屏設計。下面我將從多個方面詳細介紹這款驅動芯片。一、SSD1305 基本特性顯示分辨率:最大支持 128 segments 6…

安全為先:如何在 Python 中安全處理數據庫連接與敏感信息

安全為先:如何在 Python 中安全處理數據庫連接與敏感信息 引言:Python 與安全的數據庫交互 自 1991 年誕生以來,Python 憑借其簡潔優雅的語法和強大的生態系統,成為 Web 開發、數據科學、人工智能和數據庫交互的首選語言。作為“膠水語言”,Python 不僅讓開發者能夠快速…

服務器經常出現藍屏是什么原因導致的?如何排查和修復?

服務器出現藍屏(BSOD,Blue Screen of Death)是一個嚴重的問題,通常表明系統內核或硬件發生了不可恢復的錯誤。藍屏不僅會導致服務器宕機,還可能對業務運行造成重大影響。要有效解決藍屏問題,需要先找到根本…

為什么elementui的<el-table-column label=“名稱“ prop=“name“ label不用寫成:label

在 Vue.js 中,label 和 prop 是 el-table-column 組件的普通屬性,而不是動態綁定的表達式。因此,不需要使用 : 來綁定它們。 1. Vue.js 中的屬性綁定 在 Vue.js 中,屬性綁定有兩種方式: 靜態屬性綁定:直接寫…

分布式光纖傳感:為儲能安全保駕護航

儲能系統是指一種能夠將電能、化學能、動能等形式的能量進行轉化、儲存和釋放的裝置,廣泛應用于可再生能源發電、智能電網、電動車等領域。儲能行業這幾年得到了穩步發展,受到政府機構、行業協會、大型能源企業、電網公司、系統集成商、檢測認證機構等業…

從歷史航拍圖像中去除陰影

在光學遙感中,陰影是影響土地覆蓋制圖精度和分辨率的一個因素,無論是歷史影像(黑白影像)還是近期影像(全彩影像)。陰影的產生取決于太陽光照(太陽方位角和天頂角)、相機視點&#xf…

UE material advance 學習筆記

如何體現輪胎速度的快速感:就是增加一個radial blur,會讓視覺效果感覺輪胎已經轉冒煙了,但是上面兩個輪胎的轉速其實是相同的這種磨砂的感覺,可以用上ditherAA來實現只看法線這一塊,ditherAA就是讓他的表面顏色有大量的…

Vue--2、Vue2 項目配置與組件化開發

一、Vue2 項目環境搭建1. 環境準備安裝 Node.js:推薦使用 nvm 管理多版本 Node# 安裝Node 16.20.2 nvm install 16.20.2 # 切換至指定版本 nvm use 16.20.2 # 驗證安裝 node -v && npm -v安裝 Vue CLI 腳手架:# 國內鏡像源安裝 npm install --re…

虛幻基礎:函數的返回節點

能幫到你的話,就給個贊吧 😘 文章目錄函數的返回節點:返回執行后的值返回執行后的值若不執行第一次 返回參數的默認值第二次 返回上一次執行值示例函數的返回節點:返回執行后的值 返回執行后的值 若不執行 第一次 返回參數的默…

FFmpeg 升級指北

近期我參與了部門底層庫依賴的 FFmpeg 從 3.4 升級至 7.0.2 的工作,在此分享一些經驗和遇到的 API 變動。 將 FFmpeg 升級到高版本后,編譯過程中遇到大量報錯是常態。這些錯誤通常源于 API 接口變更或結構體字段調整。此時不必驚慌,核心解決…

RISCV Linux 虛擬內存精講系列三 -- setup_vm()

在 Linux 使用虛擬地址前,需要先配置頁表,這就是 setup_vm() 的作用。然而,Linux 的頁表配置,并不是一次過完成的,分了兩個階段,如下:在 setup_vm() 中,主要初始化了:1. …

創客匠人:解析創始人 IP 打造的底層邏輯與知識變現路徑

在數字經濟時代,創始人 IP 的價值被不斷放大,而知識變現作為 IP 商業閉環的核心環節,正成為無數創業者探索的方向。創客匠人深耕知識付費領域多年,見證了大量創始人從 0 到 1 打造 IP 并實現變現的全過程,其背后的邏輯…

Visual Studio 2022 MFC Dialog 添加Toolbar及Tips提示

主要步驟:在主框架類中添加消息處理函數聲明在 OnCreate 函數中啟用工具欄提示在消息映射中注冊 TTN_NEEDTEXT 消息使用 OnToolTipText 函數實現自定義提示文本1.在主程序的.h文件中加入afx_msg BOOL OnToolTipText(UINT id, NMHDR* pNMHDR, LRESULT* pResult); 2.在…