靜態鏈接與動態鏈接(Linux)

前言

上一篇分享了靜態鏈接與動態鏈接的實驗(Windows):C語言動態鏈接與靜態鏈接。這一篇分享Linux下的筆記,同時對上一篇筆記做一個補充。

首先,我們把靜態鏈接與動態鏈接做一個這樣子的比喻:

把鏈接過程看做我們平時學習時做筆記的過程。我們平時學習時準備一本筆記本專門記錄我們的學習筆記,比如在某本書的某一頁上看到一個很好很有用的知識,這時候我們有兩種方法記錄在我們的筆記本上,一種是直接把那一頁的內容全部抄寫一遍到筆記本上(靜態鏈接);另一種是我們在筆記本上做個簡單的記錄(動態鏈接),比如寫上:xxx知識點在《xxx》的xxx頁。

從這兩種方法中我們可以很清楚地知道兩種方式的特點,第一種方式的優點就是我們在複習的時候就很方便,不用翻閱其它書籍了,但是缺點也很明顯,就是佔用筆記本的空間很多,這種方法很快就把我們的筆記本給寫滿了。第二種方式的優點就是很省空間,缺點就是每當我們複習的時候,手頭上必須備着相關的參考書籍,比如我們去教室複習的時候,就得揹着一大摞書去複習,這樣我們複習的效率可能就沒有那麼高了。

這對應到我們的動態鏈接與靜態鏈接上是不是就很好理解了:

靜態鏈接與動態鏈接的主要優缺點

(1)靜態鏈接的優缺點:

優點:

  • 代碼裝載速度快,執行速度略比動態鏈接庫快;

缺點:

  • 使用靜態鏈接生成的可執行文件體積較大,包含相同的公共代碼,造成浪費。

(2)動態鏈接的優缺點:

優點:

  • 生成的可執行文件較靜態鏈接生成的可執行文件小;

缺點:

  • 速度比靜態鏈接慢;

動態、靜態鏈接實驗

我們先編寫如下代碼(共三個文件):

文件1(main.c):

#include "test.h"

int main(void)
{
	print_hello();
	return 0;
}

文件2(test.c):

#include "test.h"

void print_hello(void)
{
	printf("hello world\n");
}

文件3(test.h):

#ifndef __TEST_H
#define __TEST_H

#include <stdio.h>

void print_hello(void);

#endif

1、動態鏈接實驗

首先,將源文件生成目標文件(*.o),命令:

gcc -c -fPIC main.c test.c

這裏得根據實際編譯環境加上或者不加上-fPIC參數,這個是與gcc的版本有關,像我這邊的gcc 5.4.0就得顯示加上-fPIC這個參數,若是不加,則會影響下一步的鏈接過程。
======001

在Linux中,動態庫的擴展名一般爲.so。針對上面生成的test.o文件,生成動態庫的命令爲:

gcc -shared test.o -o libtest_d.so

======002

若是上一步不加-fPIC參數,則會產生如下錯誤:
======003

大概意思就是.rodata不可以拿來製作共享文件,請加上-fPIC參數重新編譯。問題分析:

-fPIC 作用於編譯階段,告訴編譯器產生與位置無關代碼(Position-Independent Code),則產生的代碼中,沒有絕對地址,全部使用相對地址,故而代碼可以被加載器加載到內存的任意位置,都可以正確的執行。這正是共享庫所要求的,共享庫被加載時,在內存的位置不是固定的。

從gcc來看,shared應該是包含fPIC選項的,但似乎不是所有的版本都支持,所以最好顯式加上fPIC選項。

使用鏈接動態庫的方式生成可執行程序,命令:

gcc main.o -L. -ltest_d -o test_d.out

======004

這裏的-L.的含義是在搜索庫文件時包含當前目錄,-ltest_d的含義是鏈接名稱爲libtest_d.so的動態鏈接庫。

下面運行test_d.out程序,發現出現如下錯誤:
=====005

不能找到共享庫文件libtest_d.so,加載失敗。因爲一般情況下Linux會在/usr/lib路徑中搜索需要用到的庫,而libtest_d.so庫並不在這個路徑下。解決方法有兩種:一種就是把這個文件拷貝至/usr/lib路徑下,但是一般不允許這樣做,一般用戶也不允許往這個路徑裏拷貝東西。另一種就是把當前路徑增加爲動態庫的搜索路徑,命令爲:

export LD_LIBRARY_PATH=./:$LD_LIBRARY_PATH

這時候就可以正常運行了:
======006

2、靜態鏈接實驗

靜態庫用ar工具來製作。ar是一個歸檔工具,用於建立、修改、提取歸檔文件(archive)。一個歸檔文件可以包含多個目標文件,也被稱爲靜態庫。在Linux下,靜態庫的擴展名一般爲.a

把目標文件test.o做成靜態庫,命令:

ar -rv libtest_s.a test.o

======007

其中rv參數爲組合參數,其中r參數表示當建立的模塊名已經存在時,則覆蓋同名模塊,v參數用來顯示附加信息,比如被處理的文件的名字。

使用鏈接靜態庫的方法生成可執行程序,命令:

gcc main.o -L. -ltest_s -o test_s.out

======008

運行程序:
======009

刪除靜態庫之後,可執行程序也是能正常運行的。事實上,使用鏈接靜態庫的方式生成的可執行程序與直接使用目標文件生成的可執行程序沒有區別。只是經過了靜態庫的鏈接,變爲了一個文件,方便於調用、移植和保存。

歸檔工具ar可以很方便地查看和刪除歸檔文件中的成員。

查看靜態庫libtest_s.a中的內容,命令:
======010

關於ar工具更多的命令參數可輸入ar --help進行查看:
======011

以上就是關於靜態鏈接與動態鏈接的Linux筆記,如有錯誤,歡迎指出!


在這裏插入圖片描述

發佈了100 篇原創文章 · 獲贊 147 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章