Linux筆記---動靜態庫(使用篇)

目錄

1. 庫的概念

2. 靜態庫(Static Libraries)

2.1 靜態庫的制作

2.2 靜態庫的使用

2.2.1 顯式指定庫文件及頭文件路徑

2.2.2?將庫文件安裝到系統目錄

2.2.3?將頭文件安裝到系統目錄

3. 動態庫

3.1 動態庫的制作

3.2 動態庫的使用

3.2.1 顯式指定庫文件路徑

2.2.2?將路徑加載到環境變量中?

2.2.3 配置文件

4. 總結與補充


1. 庫的概念

庫(Library) 是一組預先編譯好的代碼(函數、類、數據等)的集合,可以被多個程序共享和重復使用。庫的核心目的是代碼復用,避免開發者重復編寫相同的功能(如文件操作、數學計算等)。

本質上來說庫是一種可執行代碼的二進制形式,可以被操作系統載如內存執行。

按照代碼復用的形式,庫可以分為兩種:

  • 靜態庫:.a?[Linux].lib?[Windows]
  • 動態庫:.so?[Linux]、.dll?[Windows]

庫是在鏈接這一步被使用的,實際上就是一堆 .o 文件的集合,我們可以特定的工具來將這些 .o 文件進行打包,進而形成庫。?

為舉例方便,這里給出我們自己實現的簡單的C語言庫---myc:

// mystdio.h#pragma once
#include <stdio.h>
#define MAX 1024
#define NONE_FLUSH (1<<0)
#define LINE_FLUSH (1<<1)
#define FULL_FLUSH (1<<2)typedef struct IO_FILE
{int fileno;int flag;char outbuffer[MAX];int bufferlen;int flush_method;
}MyFile;MyFile *MyFopen(const char *path, const char *mode);
void MyFclose(MyFile *);
int MyFwrite(MyFile *, void *str, int len);
void MyFFlush(MyFile *);// mystdio.c#include "mystdio.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>static MyFile *BuyFile(int fd, int flag)
{MyFile *f = (MyFile*)malloc(sizeof(MyFile));if(f == NULL) return NULL;f->bufferlen = 0;f->fileno = fd;f->flag = flag;f->flush_method = LINE_FLUSH;memset(f->outbuffer, 0, sizeof(f->outbuffer));return f;
}MyFile *MyFopen(const char *path, const char *mode)
{int fd = -1;int flag = 0;if(strcmp(mode, "w") == 0){flag = O_CREAT | O_WRONLY | O_TRUNC;fd = open(path, flag, 0666);}else if(strcmp(mode, "a") == 0){flag = O_CREAT | O_WRONLY | O_APPEND;fd = open(path, flag, 0666);}else if(strcmp(mode, "r") == 0){flag = O_RDWR;fd = open(path, flag);}else{//TODO}if(fd < 0) return NULL;return BuyFile(fd, flag);
}
void MyFclose(MyFile *file)
{if(file->fileno < 0) return;MyFFlush(file);close(file->fileno);free(file);
}
int MyFwrite(MyFile *file, void *str, int len)
{// 1. 拷貝memcpy(file->outbuffer+file->bufferlen, str, len);file->bufferlen += len;// 2. 嘗試判斷是否滿足刷新條件!if((file->flush_method & LINE_FLUSH) && file->outbuffer[file->bufferlen-1] == '\n'){MyFFlush(file);}return 0;
}
void MyFFlush(MyFile *file)
{if(file->bufferlen <= 0) return;// 把數據從用戶拷貝到內核文件緩沖區中int n = write(file->fileno, file->outbuffer, file->bufferlen);(void)n;fsync(file->fileno);file->bufferlen = 0;
}// mystring.h#pragma once
int my_strlen(const char *s);// mystring.c#include "mystring.h"int my_strlen(const char *s)
{const char *start = s;while(*s){s++;}return s - start;
}

接下來,我們會介紹如何將上述的原文件打包成動靜態庫并使用。?

2. 靜態庫(Static Libraries)

  • 文件擴展名:.a(Archive)

  • 特點:
    • 在編譯時,庫的代碼會被直接復制到最終的可執行文件中。

    • 生成的可執行文件獨立,不依賴運行時環境中的庫文件。

    • 缺點:文件體積較大,且更新庫時需要重新編譯程序。

  • 創建工具:ar(歸檔工具)+ ranlib(生成索引)。

  • 使用場景:適合對程序獨立性要求高的場景。

2.1 靜態庫的制作

靜態庫使用 ar 指令進行打包:

ar -rc lib[庫名].a [目標文件s]

lib[庫名].a 是靜態庫文件的命名規范,實際上的庫名需要去掉lib前綴以及.a擴展名。

通常來說,只有庫文件是不夠的,還需要將庫的頭文件交給用戶,所以我們可以使用如下的Makefile來將庫及其頭文件一起打包交給用戶:

SRC=$(wildcard *.c)
OBJ=$(SRC:.c=.o)libmyc.a:$(OBJ)ar -rc $@ $^$(OBJ):$(SRC)gcc -c $^.PHONY:output
output:mkdir -p lib/includemkdir -p lib/mylibcp -f *.h lib/includecp -f *.a lib/mylibtar czf lib.tgz lib.PHONY:clean
clean:rm -rf *.o libmyc.a lib lib.tgz

2.2 靜態庫的使用

gcc/g++會默認鏈接c標準庫,但是myc庫是我們自己制作的第三方庫,所以在編譯時需要指定鏈接myc庫。

假設用戶已經接收到了我們的 lib.tgz 包,并且用戶的代碼(usercode.c)調用了我們庫中的方法:

注:使用tar xzf lib.tgz進行解包得到lib目錄。?

// usercode.c#include "mystdio.h"
#include "mystring.h"
#include <string.h>
#include <unistd.h>int main()
{MyFile *filep = MyFopen("./log.txt", "a");if(!filep){printf("fopen error!\n");return 1;}int cnt = 10;while(cnt--){char *msg = (char*)"hello myfile!!!";MyFwrite(filep, msg, strlen(msg));MyFFlush(filep);printf("buffer: %s\n", filep->outbuffer);sleep(1);}MyFclose(filep); // FILE *fpconst char *str = "hello bit!\n";printf("strlen: %d\n",my_strlen(str));return 0;
}
2.2.1 顯式指定庫文件及頭文件路徑

在編譯時,需要指定頭文件所在路徑、要鏈接的庫文件路徑以及指定庫文件:

gcc -o [可執行程序] [目標文件s] -I [頭文件路徑] -L [庫路徑] -l [庫名]

2.2.2?將庫文件安裝到系統目錄

我們知道,所謂安裝,實際上就是把文件拷貝到指定的系統目錄下。這樣,在我們未顯式指定庫文件所在目錄時,系統就能夠在默認目錄中找到。

當然,除了拷貝,建立鏈接也是可以的。?

  • /lib/usr/lib:系統級庫
  • /usr/local/lib:用戶安裝的第三方庫

我們將 libmyc.a 文件拷貝到三個庫中的一個即可完成安裝,此時不在需要指明庫所在路徑:

但是,不建議安裝到系統級庫,用戶自己要安裝的第三方庫最好安裝到 /usr/local/lib 中。?

2.2.3?將頭文件安裝到系統目錄
  • /usr/include:系統級頭文件
  • /usr/local/include:本地安裝的第三方庫頭文件
  • /usr/include/<庫名> 或 /usr/local/include/<庫名>:特定軟件的子目錄

我們將自己的頭文件拷貝到上述目錄下即可完成安裝,此時不再需要指明頭文件所在路徑:

3. 動態庫

  • 文件擴展名:.so(Shared Object)

  • 特點:
    • 在程序運行時被動態加載到內存,多個程序可共享同一份庫代碼。

    • 可執行文件體積小,庫更新時無需重新編譯程序。

    • 缺點:依賴運行時環境中的庫文件(若缺失會導致程序無法運行)。

  • 創建工具:gcc/g++ 的 -shared 選項。

  • 使用場景:大多數系統庫(如 glibc)和通用功能庫(如 OpenSSL)。

3.1 動態庫的制作

// 編譯目標文件時需要帶上-fPIC選項,fPIC:產生位置無關碼(position independent code) 
gcc/g++ -c -fPIC [原文件s]// 生成庫文件時需要帶上-shared選項,shared: 表示生成共享庫格式 
gcc/g++ -o lib[庫名].so [目標文件s] -shared

同樣的,lib[庫名].so 是命名規范,實際上的庫名需要去掉lib前綴和 .so擴展名。

我們可以使用如下的Makefile來對庫及其頭文件進行打包:

SRC=$(wildcard *.c)
OBJ=$(SRC:.c=.o)libmyc.so:$(OBJ)gcc -shared -o $@ $^$(OBJ):$(SRC)gcc -fPIC -c $^.PHONY:output
output:mkdir -p lib/includemkdir -p lib/mylibcp -f *.h lib/includecp -f *.so lib/mylibtar czf lib.tgz lib.PHONY:clean
clean:rm -rf *.o libmyc.so lib lib.tgz

3.2 動態庫的使用

我們以同樣的代碼作為示例,將庫及頭文件安裝到系統目錄的方式與靜態庫一樣,這里就不再重復,但是對于顯式給出庫文件路徑的方式,我們要多說兩句。

3.2.1 顯式指定庫文件路徑

假如我們未將庫文件安裝到系統目錄當中,并顯式指定某路徑下的庫文件:

我們會發現編譯通過了,但是:

當我們運行生成的可執行程序時,會發現系統顯式找不到對應的庫。

這是因為,我們僅僅告訴了編譯器:“這個庫是存在的”,所以編譯器完成了編譯。

但是動態鏈接是在程序運行時才將庫與可執行程序產生鏈接,負責鏈接的是系統,然而系統并不知道在哪里找到這個庫。?

要讓操作系統在運行我們的程序時找到對應的動態庫,我們可以選擇安裝的形式(與靜態庫的安裝完全一致),也可采取以下幾點中提到的措施。

?注意:與靜態鏈接不同,接下來的幾點措施(包括安裝),都不需要重新編譯可執行文件。

2.2.2?將路徑加載到環境變量中?
# LD_LIBRARY_PATH:臨時指定額外的庫搜索路徑。
export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/path/to/libs

使用 ldd 命令可以查看可執行程序鏈接的庫及其所在路徑:

但是,這種方式只是臨時的,重新登錄或更新環境變量時就會失效。?

2.2.3 配置文件
  • /etc/ld.so.conf:系統級庫路徑配置文件
  • /etc/ld.so.conf.d:用戶庫路徑配置文件目錄

我們可以直接在/etc/ld.so.conf中加入我們庫文件的路徑,但是我們依然更建議在用戶庫路徑配置文件目錄中添加自己的配置文件:

這里sudo echo創建文件的方式居然不行,只能用編輯器創建了。

然后加載配置文件:

sudo ldconfig

?結果與2.2.2相同,這里就不展示了。

4. 總結與補充

  • gcc/g++編譯命令補充:
    • [-I] :指定頭文件所在目錄。

    • [-L]:指定庫文件所在路徑。

    • [-l]:指定要鏈接的庫。

    • [-shared]:生成動態庫。

    • [-fPIC]:產生位置無關碼。

    • [-static]:使用靜態鏈接。

  • 靜態庫使用ar命令進行打包:
    ar -rc lib[庫名].a [目標文件s]
  • 將靜態庫與用戶目標文件一起編譯即可生成可執行程序。
  • ?動態庫使用gcc/g++進行打包,且目標文件需要攜帶位置無關碼:
    // 編譯目標文件時需要帶上-fPIC選項,fPIC:產生位置無關碼(position independent code) 
    gcc/g++ -c -fPIC [原文件s]// 生成庫文件時需要帶上-shared選項,shared: 表示生成共享庫格式 
    gcc/g++ -o lib[庫名].so [目標文件s] -shared
  • 動態庫在編譯時需要讓gcc/g++知道這個庫是存在的(給出路徑或安裝到系統,并指定庫名)。在運行時,系統需要能夠找到這個庫(需要安裝到系統)。
  • 第三方庫在編譯時要指定鏈接這個庫。
  • 在編譯時,我們的系統當中可能既安裝了某個庫的動態版本,又安裝了某個庫的靜態版本。此時,編譯器默認能采用動態鏈接則采用動態鏈接。如果要使用靜態鏈接則需要帶上 -static 選項,一旦帶上這個選項,就意味著動態鏈接被禁用,如果某個庫只有動態鏈接的版本,則會發生鏈接失敗。

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

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

相關文章

Java并發編程2(鎖-Sychronized)

目錄 認識Java對象頭 sychronized鎖原理 基本概念 工作原理 1.作用在方法上 2.作用在代碼塊上 工作機制 JVM優化鎖 Monitor鎖 wait/notify park/unpark 線程狀態轉換案例 死鎖 概念 死鎖發生的必要條件 哲學家問題 活鎖 饑餓 概念 饑餓的原因 Reentrant…

現階段高校的人工智能方案培訓如何?

人工智能在未來肯定是核心發展力&#xff0c;核心競爭力&#xff0c;也是國家重點扶持的對象&#xff0c;但我還是不看好高校的人工智能方向&#xff0c;只是怕有些同學對市場前景盲目樂觀&#xff0c;就輕易上車了。 你要是985以上的高校&#xff0c;可以考慮選擇人工智能&…

JavaScript中的繼承有哪些方式?各有什么優缺點

在 JavaScript 中&#xff0c;繼承主要通過原型鏈實現&#xff0c;常見的繼承方式有以下幾種&#xff0c;每種方式都有其優缺點&#xff1a; 1. 原型鏈繼承 1. 實現方式&#xff1a;將子類的原型對象指向父類的實例。 function Parent() {} function Child() {} Child.protot…

深入理解指針(3)(C語言版)

文章目錄 前言 一、字符指針變量二、數組指針變量2.1 數組指針變量是什么2.2 數組指針變量怎么初始化2.2.1 靜態初始化2.2.2 動態初始化 三、二維數組傳參的本質四、函數指針變量4.1 函數指針變量的創建4.2 函數指針變量的使用4.3 typedef關鍵字4.4拓展 五、函數指針數組六、轉…

Linux之 權限提升(Linux Privilege Escalation)

Linux 之權限提升 系統信息 1.獲取操作系統信息 2.檢查PATH&#xff0c;是否有任何可寫的文件夾&#xff1f; 3.檢查環境變量&#xff0c;有任何敏感細節嗎&#xff1f; 4.使用腳本&#xff08;DirtyCow&#xff1f;&#xff09;搜索內核漏洞 5.檢查sudo 版本是否存在漏洞…

【leetcode hot 100 215】數組中的第K個最大元素

解法一&#xff1a;維護最大最小值 -> 堆 -> k個元素的最小值堆 class Solution {public int findKthLargest(int[] nums, int k) {// 維護最大最小值 -> 堆 -> k個元素的最小值堆PriorityQueue<Integer> heap new PriorityQueue<>((n1, n2) -> n…

csp信奧賽C++常用的數學函數詳解

csp信奧賽C常用的數學函數詳解 在信息學奧林匹克競賽&#xff08;信奧賽&#xff09;中&#xff0c;C 的 <cmath> 頭文件提供了豐富的數學函數&#xff0c;用于高效處理數學運算。以下是常用系統數學函數的詳細講解及匯總表格。 絕對值函數 int abs(int x)&#xff1a;返…

Java IntelliJ IDEA 中配置多個 JDK 版本

目錄 一、添加多個 JDK 版本1. 下載并安裝多個 JDK 版本2. 配置 JDK 在 IntelliJ IDEA 中 二、在項目中切換 JDK 版本1. 設置項目使用的 JDK 版本2. 設置模塊使用的 JDK 版本 三、在運行配置中指定 JDK 版本四、總結 在實際開發中&#xff0c;我們常常需要在同一個項目中使用不…

ChatDBA VS DeepSeek:快速診斷 OceanBase 集群新租戶數據同步異常

社區王牌專欄《一問一實驗&#xff1a;AI 版》改版以來已發布多期&#xff08;51-60&#xff09;&#xff0c;展現了 ChatDBA 在多種場景下解決問題的效果。 下面讓我們正式進入《一問一實驗&#xff1a;AI 版》第 62 期&#xff0c;看看 ChatDBA 最新效果以及與熱門大模型 De…

Java條碼與二維碼生成技術詳解

一、技術選型分析 1.1 條碼生成方案 Barbecue是最成熟的Java條碼庫&#xff0c;支持&#xff1a; Code 128EAN-13/UPC-AUSPS Inteligent Mail等12種工業標準格式 1.2 二維碼方案對比 庫名稱維護狀態復雜度功能擴展性ZXing★★★★☆較高強QRGen★★★☆☆簡單一般BoofCV★…

air780eq 阿里云

硬件&#xff1a;APM32F030C8 Air 780eq 參考文檔&#xff1a; 合宙780E-4G模塊通過AT指令連接到阿里云平臺&#xff0c;實現信息的收發_air780e上傳阿里云屬性值at命令-CSDN博客 阿里云 - atair780eq - 合宙文檔中心 4G模塊接入阿里云-實現數據上傳和命令下發_4g模塊上傳…

oracle數據庫(數據庫啟動關閉/sqlplus登錄及基本操作/設置字符集/distinct去重)

目錄 1. Oracle數據庫啟動 2. Oracle數據庫關閉 3. sqlplus登錄Oracle數據庫 3.1 使用sqlplus登錄Oracle數據庫 3.2 使用sqlplus登錄Oracle數據庫 3.3 遠程登錄 3.4 解鎖用戶 3.5 修改用戶密碼 3.6 查看當前語言環境 4. sqlplus基本操作 4.1 顯示當前用戶 4.2 查看當前用戶…

Java 大視界 -- Java 大數據在智能金融區塊鏈跨境支付與結算中的應用(154)

&#x1f496;親愛的朋友們&#xff0c;熱烈歡迎來到 青云交的博客&#xff01;能與諸位在此相逢&#xff0c;我倍感榮幸。在這飛速更迭的時代&#xff0c;我們都渴望一方心靈凈土&#xff0c;而 我的博客 正是這樣溫暖的所在。這里為你呈上趣味與實用兼具的知識&#xff0c;也…

大模型詞表注入

大模型詞表注入&#xff08;Vocabulary Injection&#xff09; 大模型詞表注入&#xff08;Vocabulary Injection&#xff09;是指在預訓練語言模型&#xff08;如GPT、LLAMA等&#xff09;的基礎上&#xff0c;動態擴展其詞表&#xff08;Vocabulary&#xff09;的技術&#…

在Cesium中使用ThreeJs材質(不是場景融合哦)

在Cesium中使用ThreeJs材質(不是場景融合哦&#xff09;_嗶哩嗶哩_bilibili

初教六雙機一飛沖天動作要領

初教六雙機一飛沖天動作要領 初教六雙機“一飛沖天”是典型的垂直爬升特技動作&#xff0c;要求雙機以近乎垂直的姿態同步高速爬升&#xff0c;展現飛機的動力性能與編隊協同能力。以下是該動作的詳細技術解析與執行要點&#xff1a; 一、動作定義與特點 基本形態 雙機以相同速…

給Web開發者的HarmonyOS指南02-布局樣式

給Web開發者的HarmonyOS指南02-布局樣式 本系列教程適合鴻蒙 HarmonyOS 初學者&#xff0c;為那些熟悉用 HTML 與 CSS 語法的 Web 前端開發者準備的。 本系列教程會將 HTML/CSS 代碼片段替換為等價的 HarmonyOS/ArkUI 代碼。 布局基礎對比 在Web開發中&#xff0c;我們使用CS…

京東軟件測試崗位經典面試題(附答案)

1、黑盒測試的測試用例常見設計方法都有哪些&#xff1f;請分別以具體的例子來說明這些方法在測試用例設計工作中的應用。 1&#xff09;等價類劃分&#xff1a;等價類是指某個輸入域的子集合.在該子集合中&#xff0c;各個輸入數據對于揭露程序中的錯誤都是等效的.并合理地假…

3.26[a]paracompute homework

5555 負載不平衡指多個線程的計算量差異顯著&#xff0c;導致部分線程空轉或等待&#xff0c;降低并行效率。其核心矛盾在于任務劃分的靜態性與計算動態性不匹配&#xff0c;尤其在處理不規則數據或動態任務時尤為突出。以稀疏矩陣的向量乘法為例&#xff0c;假設其非零元素分…

網站安全專欄-------淺談CC攻擊和DDoS攻擊的區別

CC攻擊和DDoS攻擊都是網絡攻擊的類型&#xff0c;但它們在攻擊方式、目標和效果上有所不同。以下是它們之間的一些主要區別&#xff1a; ### 1. 定義 - **DDoS攻擊&#xff08;分布式拒絕服務攻擊&#xff09;**&#xff1a; DDoS攻擊是指攻擊者通過大量的分布式計算機&#x…