天天看點

一篇很不錯的介紹靜态連接配接和動态連結的文章

确切來說,這是我轉載的别人轉載的一篇文章,我轉載自:http://blog.csdn.net/ytz_linuxer/article/details/4740512 。

Linux下的.so是基于Linux下的動态連結,其功能和作用類似與windows下.dll檔案。

下面是關于.so的介紹:

一、引言

通常情況下,對函數庫的連結是放在編譯時期(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

動态連結庫*.so的編譯與使用- -

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

1、動态庫的編譯

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

so_test.h:

#include   "stdio.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。

test.c:

#include "so_test.h"

int main()

{

test_a();

test_b();

test_c();

return 0;

}

l 将test.c與動态庫libtest.so連結生成執行檔案test:

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

l 測試是否動态連接配接,如果列出libtest.so,那麼應該是連接配接正常了

$ ldd test

l 執行test,可以看到它是如何調用動态庫中的函數的。

3、編譯參數解析

最主要的是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檔案來指定動态庫的目錄。通常這樣做就可以解決庫無法連結的問題了。

此文轉載于http://blog.csdn.net/lwhsyit/archive/2008/09/01/2860964.aspx 本人作了稍微的修改

最後要注意的問題是,該test程式時,可能會提示 error while loading shared libraries: libtest.so: cannot open shared object file: No such file or directory

這時候,隻需要把已經編譯好的libtest.so拷貝到/usr/lib裡面就OK了,也就是說,在編譯的時候,要注意可執行程式的路徑!

繼續閱讀