Linux共享庫的創建

庫是程序員必須的工具,它是已經編譯過的代碼。庫通常提供通用的功能,例如鏈表和二叉樹等數據結構,或者特定的功能,例如MySQL等數據庫服務器訪問接口。

         多數大型軟件項目有多個部件組成,這些部件有可能在其它項目中用到,或者你只是想分離多個部件以便組織。當你有一個可重用或者邏輯上獨立的函數集合,建立一個庫使你不用總是複製代碼到現在的項目然後重新編譯,而且可以保持不同的模塊的分離,修改其中一個不會影響其他模塊。只要一個庫被寫好且測試過,就可以安全地重用。

         建立靜態庫是相當簡單的,這裏只講如何建立共享庫(動態庫)。

         在開始之前,先看看從源代碼到可執行程序這個過程中發生什麼:

         1、C語言預處理:這個階段,任何以#開頭,例如#define和#include的行都會被處理,也就是宏替換和文件包含。

         2、編譯:將預處理後的源代碼轉換成彙編代碼,然後在轉換成機器代碼。

         3、鏈接:將objectfiles和庫鏈接生成可執行程序。對於靜態庫,鏈接器把庫本身放到最終的可執行程序中;而對於共享庫,只是把對庫的引用放到裏面。

         4、裝入:當我們執行程序的時候,檢查程序對共享庫的引用,將動態庫映射到程序中。

第3和第4步是共享庫比較神奇卻也讓人困惑的地方。

foo.h:

1 #ifndef foo_h__
2 #define foo_h__
3 extern void foo(void);
4 #endif  // foo_h__

foo.c:

#include <stdio.h>
void foo(void)
{
    puts("Hello, I'm a shared library");
}

main.c:

複製代碼
#include <stdio.h>
#include "foo.h"
int main(void)
{
    puts("This is a shared library test...");
    foo();
    return 0;
}
複製代碼

foo.h定義了我們的庫的接口,一個函數foo(),foo.c是函數實現的地方。

本例子在路徑 /home/username/foo

Step 1:編譯不依賴於位置的代碼

將庫的源代碼編譯成位置無關的代碼

$ gcc -c -Wall -Werror -fpic foo.c

Step 2: 由目標文件(object file)生成共享庫

將庫的名稱定爲 libfoo.so:

gcc -shared -o libfoo.so foo.o

Step 3: 鏈接共享庫

先編譯 main.c然後鏈接 libfoo最終生成程序“test.”-lfoo選項是用來尋找libfoo.so的。 GCC 假定所有庫以lib開頭,以.so或者.a結尾(.so是代表共享庫,.a代表歸檔或者靜態鏈接庫)。

$ gcc -Wall -o test main.c -lfoo
/usr/bin/ld: cannot find -lfoo
collect2: ld returned 1 exit status

告訴GCC去哪裏找共享庫

–L選項,在此例中,共享庫在當前目錄,也就是/home/username/foo

$ gcc -L/home/username/foo -Wall -o test main.c -lfoo

Step 4: 使共享庫在程序運行時能用上。

$ ./test
./test: error while loading shared libraries: libfoo.so: cannot open shared object file: No such file or directory

因爲沒有將共享庫libfoo.so放在標準目錄,所以要給裝入器(loader)點幫助。有幾個選擇:可以用環境變量LD_LIBRARY_PATH,或者rpath。先看看LD_LIBRARY_PATH

Using LD_LIBRARY_PATH

$ echo $LD_LIBRARY_PATH

這個變量中什麼也沒有,我們將工作目錄加到LD_LIBRARY_PATH中。

$ LD_LIBRARY_PATH=/home/username/foo:$LD_LIBRARY_PATH
$ ./test
./test: error while loading shared libraries: libfoo.so: cannot open shared object file: No such file or directory

雖然正在LD_LIBRARY_PATH這個目錄中,但是沒有export它。在Linux,如果沒有export那些對環境變量的改變,它們的改變不會被子進程繼承,所以裝入器(loader)和test程序沒有繼承這些改變。好在,很容易解決這個問題:

$ export LD_LIBRARY_PATH=/home/username/foo:$LD_LIBRARY_PATH
$ ./test
This is a shared library test...
Hello, I'm a shared library

Good, it worked! LD_LIBRARY_PATH 用來測試共享庫很方便,尤其是當我們沒有系統管理員權限的時候。然而,exportLD_LIBRARY_PATH意味着有可能導致其他用到LD_LIBRARY_PATH的程序出問題。

Using rpath

現在試試rpath(首先清空LD_LIBRARY_PATH,以確保是rpath找到共享庫)。rpath,也就是運行路徑(run path),是在可執行程序中嵌入共享庫位置的一種方式,而不是依賴於默認的位置或者環境變量。在鏈接的階段,用“-Wl,-rpath=/home/username/foo” 選項,-WL給定鏈接器一些選項,這些選項以逗號分隔。

$ unset LD_LIBRARY_PATH
$ gcc -L/home/username/foo -Wl,-rpath=/home/username/foo -Wall -o test main.c -lfoo
$ ./test
This is a shared library test...
Hello, I'm a shared library

rpath很好用,因爲每個程序可以設定獨立的共享庫位置,而不會像LD_LIBRARY_PATH那樣影響其他程序。不過,rpath缺乏靈活性,它要求共享庫安裝到特定的路徑,在系統配置時不靈活。

Using ldconfig to modify ld.so

如果想安裝我們的庫讓任何系統登錄用戶可以使用,需要管理員權限,理由如下:首先,要把庫放到標準位置,可能是/usr/lib/usr/local/lib,普通用戶沒有寫入權限;其次,要修改ld.so配置文件和緩存。以管理員登陸,執行以下命令:

$ cp /home/username/foo/libfoo.so /usr/lib
$ chmod 0755 /usr/lib/libfoo.so

現在,庫文件在標準路徑下,有着可以被任何人讀取的權限。要告訴裝入器它是可用的,於是更新緩存:

$ ldconfig

上面的命令會創造一個到共享庫的鏈接,更新緩存。檢測以下:

$ ldconfig -p | grep foo
libfoo.so (libc6) => /usr/lib/libfoo.so

可見我們的庫已經安裝好了。在用它之前,先清理LD_LIBRARY_PATH,以防萬一:

$ unset LD_LIBRARY_PATH

重新鏈接我們的可執行程序,不需要–L選項,因爲我們的庫已經放在默認的位置,且不需要rpath選項:

$ gcc -Wall -o test main.c -lfoo

ldd命令,看看test是否使用了我們的庫:

$ ldd test | grep foo
libfoo.so => /usr/lib/libfoo.so (0x00a42000)

然後運行:

$ ./test
This is a shared library test...
Hello, I'm a shared library

鏈接:http://www.cnblogs.com/wongzawing/archive/2013/02/19/2917792.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章