搭建測試環境linux靜態連結庫與動態連結程式庫的區別及動態庫的建立

來源:互聯網
上載者:User

http://hi.baidu.com/zhengshaohua1987/blog/item/c99262eefe1e04ebb2fb95ee.html

 

一、引言
通常情況下,對函數庫的連結是放在編譯時間期(compile time)完成的。所有相關的對象檔案(object file)與牽涉到的函數庫(library)被連結合成一個可執行檔(executable file)。程式在運行時,與函數庫再無瓜葛,因為所有需要的函數已拷貝到自己門下。所以這些函數庫被成為靜態庫(static libaray),通常檔案名稱為“libxxx.a”的形式。

其實,我們也可以把對一些庫函數的連結載入延遲到程式啟動並執行時期(runtime)。這就是如雷貫耳的動態連結程式庫(dynamic link library)技術。

二、動態連結程式庫的特點與優勢

首先讓我們來看一下,把庫函數延遲到程式運行時期載入的好處:

1. 可以實現進程之間的資源共用。

什麼概念呢?就是說,某個程式的在運行中要調用某個動態連結程式庫函數的時候,作業系統首先會查看所有正在啟動並執行程式,看在記憶體裡是否已有此庫函數的拷貝了。如果有,則讓其共用那一個拷貝;只有沒有才連結載入。這樣的模式雖然會帶來一些“動態連結”額外的開銷,卻大大的節省了系統的記憶體資源。C的標準庫就是動態連結程式庫,也就是說系統中所有啟動並執行程式共用著同一個C標準庫的程式碼片段。

2. 將一些程式升級變得簡單。使用者只需要升級動態連結程式庫,而無需重新編譯連結其他原有的代碼就可以完成整個程式的升級。Windows 就是一個很好的例子。

3. 甚至可以真正坐到連結載入完全由程式員在程式碼中控制。

程式員在編寫程式的時候,可以明確的指明什麼時候或者什麼情況下,連結載入哪個動態連結程式庫函數。你可以有一個相當大的軟體,但每次啟動並執行時候,由於不同的操作需求,只有一小部分程式被載入記憶體。所有的函數本著“有需求才調入”的原則,於是大大節省了系統資源。比如現在的軟體通常都能開啟若干種不同類型的檔案,這些讀寫操作通常都用動態連結程式庫來實現。在一次運行當中,一般只有一種類型的檔案將會被開啟。所以直到程式知道檔案的類型以後再載入相應的讀寫函數,而不是一開始就將所有的讀寫函數都載入,然後才發覺在整個程式中根本沒有用到它們。

三、動態連結程式庫的建立

由於動態連結程式庫函數的共用特性,它們不會被拷貝到可執行檔中。在編譯的時候,編譯器只會做一些函數名之類的檢查。在程式啟動並執行時候,被調用的動態連結程式庫函數被安置在記憶體的某個地方,所有調用它的程式將指向這個程式碼片段。因此,這些代碼必須實用相對位址,而不是絕對位址。在編譯的時候,我們需要告訴編譯器,這些對象檔案是用來做動態連結程式庫的,所以要用地址不無關代碼(Position Independent Code (PIC))。

對gcc編譯器,只需添加上 -fPIC 標籤,如:

gcc -fPIC -c file1.c
gcc -fPIC -c file2.c
gcc -shared libxxx.so file1.o file2.o

注意到最後一行,-shared 標籤告訴編譯器這是要建立動態連結程式庫。這與靜態連結庫的建立很不一樣,後者用的是 ar 命令。也注意到,動態連結程式庫的名字形式為 “libxxx.so” 尾碼名為 “.so”

四、動態連結程式庫的使用

使用動態連結程式庫,首先需要在編譯期間讓編譯器檢查一些文法與定義。

這與靜態庫的實用基本一樣,用的是 -Lpath 和 -lxxx 標籤。如:

gcc file1.o file2.o -Lpath -lxxx -o program.exe

編譯器會先在path檔案夾下搜尋libxxx.so檔案,如果沒有找到,繼續搜尋libxxx.a(靜態庫)。

在程式運行期間,也需要告訴系統去哪裡找你的動態連結程式庫檔案。在UNIX下是通過定義名為 LD_LIBRARY_PATH 的環境變數來實現的。只需將path賦值給此變數即可。csh 命令為:

setenv LD_LIBRARY_PATH your/full/path/to/dll

一切安排妥當後,你可以用 ldd 命令檢查是否串連正常。

ldd program.exe

編譯參數解析

最主要的是GCC命令列的一個選項:
-shared 該選項指定產生動態串連庫(讓連接器產生T類型的匯出符號表,有時候也產生弱串連W類型的匯出符號),不用該標誌外部程式無法串連。相當於一個可執行檔

l -fPIC:表示編譯為位置獨立的代碼,不用此選項的話編譯後的代碼是位置相關的所以動態載入時是通過代碼拷貝的方式來滿足不同進程的需要,而不能達到真正程式碼片段共用的目的。

l -L.:表示要串連的庫在目前的目錄中

l -ltest:編譯器尋找動態串連庫時有隱含的命名規則,即在給出的名字前面加上lib,後面加上.so來確定庫的名稱

l LD_LIBRARY_PATH:這個環境變數指示動態連接器可以裝載動態庫的路徑。

l 當然如果有root許可權的話,可以修改/etc/ld.so.conf檔案,然後調用 /sbin/ldconfig來達到同樣的目的,不過如果沒有root許可權,那麼只能採用輸出LD_LIBRARY_PATH的方法了。
4、注意

調用動態庫的時候有幾個問題會經常碰到,有時,明明已經將庫的標頭檔所在目錄 通過 “-I” include進來了,庫所在檔案通過 “-L”參數引導,並指定了“-l”的庫名,但通過ldd命令察看時,就是死活找不到你指定連結的so檔案,這時你要作的就是通過修改 LD_LIBRARY_PATH或者/etc/ld.so.conf檔案來指定動態庫的目錄。通常這樣做就可以解決庫無法連結的問題了。

五、靜態庫的建立和使用:

1、產生靜態庫 :庫名 libmylib.a

ar rcs libmylib.a mylib.o

2、將靜態庫copy到 /usr/lib/ 或/lib/ 目錄下

cp libmylib.a /usr/lib/

3、靜態庫的使用

比如測試檔案為test.c

gcc -0 test test.c -lmylib

-l為選項, mylib為庫名。mylib為libmylib的中間部分,Linux下約定所有庫都以首碼lib開始

靜態庫以.a結尾,動態庫以.so結尾。再編譯程式時,無需帶上首碼和尾碼。

注意:靜態庫的命名需要以"lib"開頭,否者串連是編譯器無法找到庫

 

動態連結程式庫*.so的編譯與使用

動態庫*.so在linux下用c和c++編程時經常會碰到,最近在網站找了幾篇文章介紹動態庫的編譯和連結,總算搞懂了這個之前一直不太瞭解得東東,這裡做個筆記,也為其它正為動態庫連結庫而苦惱的兄弟們提供一點協助。
動態庫的編譯

下面通過一個例子來介紹如何產生一個動態庫。這裡有一個標頭檔:so_test.h,三個.c檔案:test_a.c、test_b.c、test_c.c,我們將這幾個檔案編譯成一個動態庫:libtest.so。

Cpp代碼 
  1. so_test.h: 
  2.  
  3. void test_a(); 
  4. void test_b(); 
  5. void test_c(); 
  6.  
  7. test_a.c: 
  8.  
  9. #include "so_test.h" 
  10.  
  11. void test_a() 
  12.     printf("this is in test_a...\n"); 
  13.  
  14. test_b.c: 
  15.  
  16. #include "so_test.h" 
  17.  
  18. void test_b() 
  19.     printf("this is in test_b...\n"); 
  20.  
  21. test_a.c: 
  22.  
  23. #include "so_test.h" 
  24.  
  25. void test_c() 
  26.     printf("this is in test_c...\n"); 
so_test.h:void test_a();void test_b();void test_c();test_a.c:#include "so_test.h"void test_a(){    printf("this is in test_a...\n");}test_b.c:#include "so_test.h"void test_b(){    printf("this is in test_b...\n");}test_a.c:#include "so_test.h"void test_c(){    printf("this is in test_c...\n");}


將這幾個檔案編譯成一個動態庫:libtest.so
gcc test_a.c test_b.c test_c.c -fPIC -shared -o libtest.so

2、動態庫的連結

在1、中,我們已經成功產生了一個自己的動態連結程式庫libtest.so,下面我們通過一個程式來調用這個庫裡的函數。程式的源檔案為:test.c。

Cpp代碼 
  1. test.c: 
  2.  
  3. #include "so_test.h" 
  4.  
  5. int main() 
  6.     test_a(); 
  7.     test_b();  
  8.     test_c(); 
  9.     return 0; 
test.c:#include "so_test.h"int main(){    test_a();    test_b();     test_c();    return 0;}

將test.c與動態庫libtest.so連結產生執行檔案test:

gcc test.c -L. -ltest -o test

測試是否動態串連,如果列出libtest.so,那麼應該是串連正常了

ldd test

l 執行test,可以看到它是如何調用動態庫中的函數的。
3、編譯參數解析
最主要的是GCC命令列的一個選項:
-shared 該選項指定產生動態串連庫(讓連接器產生T類型的匯出符號表,有時候也產生弱串連W類型的匯出符號),不用該標誌外部程式無法串連。相當於一個可執行檔

-fPIC:表示編譯為位置獨立的代碼,不用此選項的話編譯後的代碼是位置相關的所以動態載入時是通過代碼拷貝的方式來滿足不同進程的需要,而不能達到真正程式碼片段共用的目的。

-L.:表示要串連的庫在目前的目錄中

-ltest:編譯器尋找動態串連庫時有隱含的命名規則,即在給出的名字前面加上lib,後面加上.so來確定庫的名稱

LD_LIBRARY_PATH:這個環境變數指示動態連接器可以裝載動態庫的路徑。

當然如果有root許可權的話,可以修改/etc/ld.so.conf檔案,然後調用 /sbin/ldconfig來達到同樣的目的,不過如果沒有root許可權,那麼只能採用輸出LD_LIBRARY_PATH的方法了。
4、注意

調用動態庫的時候有幾個問題會經常碰到,有時,明明已經將庫的標頭檔所在目錄 通過 “-I” include進來了,庫所在檔案通過 “-L”參數引導,並指定了“-l”的庫名,但通過ldd命令察看時,就是死活找不到你指定連結的so檔案,這時你要作的就是通過修改 LD_LIBRARY_PATH或者/etc/ld.so.conf檔案來指定動態庫的目錄。通常這樣做就可以解決庫無法連結的問題了。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.