共享庫的編譯、鏈接和運行

通過例子說明靜態庫、動態庫以及鏈接和運行時庫:

有如下的目錄:

hejinxin@google:~/Mytest$ ls project/
lib1  lib2   test.c
hejinxin@google:~/Mytest$ cd project/
hejinxin@google:~/Mytest/project$ ls lib*
lib1:
say1.c  
lib2:
say2.c
在project目錄下有lib1、lib2子目錄和文件test.c,lib1和lib2下面分別有文件say1.c和say2.c,內容如下:

/*************************************************************************
	> File Name: say1.c
	> Author: jxhe
	> Created Time: 2015年01月22日 星期四 17時55分16秒
 ************************************************************************/

#include<stdio.h>
void say()
{
	printf("Say1!");
}
/*************************************************************************
	> File Name: say2.c
	> Author: jxhe
	> Created Time: 2015年01月22日 星期四 17時55分16秒
 ************************************************************************/

#include<stdio.h>
void say()
{
	printf("Say2!");
}
在project下面的test.c是調用lib1中的say()函數或者lib2中的say()。

/*************************************************************************
	> File Name: test.c
	> Author: jxhe
	> Created Time: 2015年01月22日 星期四 17時57分08秒
 ************************************************************************/

#include<stdio.h>
int main()
{
	say();
	return 0;
}
下面分別在lib1和lib2下編譯.c文件,但是都生成同一個庫名。

hejinxin@google:~/Mytest/project$ cd lib1/
hejinxin@google:~/Mytest/project/lib1$ ls
say1.c 
hejinxin@google:~/Mytest/project/lib1$ gcc -o libsay.so -fPIC -shared say1.c
hejinxin@google:~/Mytest/project/lib1$ ls
libsay.so  say1.c
文件say2.c也作同樣的編譯。
現在通過指定共享庫的路徑,編譯test可執行文件。

hejinxin@google:~/Mytest/project/lib1$ cd ..
hejinxin@google:~/Mytest/project$ ls
lib1  lib2  test.c
hejinxin@google:~/Mytest/project$ gcc -o test -Llib1 -lsay test.c 
hejinxin@google:~/Mytest/project$ ls
lib1  lib2  test  test.c
hejinxin@google:~/Mytest/project$ ./test 
./test: error while loading shared libraries: libsay.so: cannot open shared object file: No such file or directory
hejinxin@google:~/Mytest/project$ ldd test
linux-vdso.so.1 => (0x00007fff54945000)
libsay.so => not found
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fd5eb7e6000)
/lib64/ld-linux-x86-64.so.2 (0x00007fd5ebbbf000)


在執行test程序是報錯,說沒有libsay這個共享庫。但是我們知道在編譯是通過-L指定庫在lib1下面,爲什麼ldd工具分析得出的結論是not found?這是因爲程序執行時,查找庫的路徑和編譯時指定的不同。有幾種方法能修補現在的錯誤。一是,將包含目標共享庫的路徑添加到/etc/ld.so.conf,然後執行ldconfig。ldconfig的作用是讀取ld.so.conf的內容到ld.so.cache中,然後程序執行中遇到調用共享庫的函數時,就按照這個文件中提供的路徑去查找;第二個方法是,將共享庫所在的路徑添加到環境變量LD_LIBRARY_PATH中。
hejinxin@google:~/Mytest/project$ cat /etc/ld.so.conf
include /etc/ld.so.conf.d/*.conf
/home/hejinxin/Mytest/project/lib1
在加入到ld.so.conf之後,再調用ldd和執行程序查看結果。

hejinxin@google:~/Mytest</project$ ldd test
linux-vdso.so.1 =>  (0x00007fff792aa000)
libsay.so => /home/hejinxin/Mytarpackeg/project/lib1/libsay.so (0x00007fb43ecda000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fb43e91b000)
/lib64/ld-linux-x86-64.so.2 (0x00007fb43eef4000)
hejinxin@google:~/Mytest/project$ ./test
Say1!
可以看到效果了,libsay.so => 指向的地址正是這個共享庫的目錄,而不再是not found。test執行的結果是Say1!。那麼,如果我們需要更新共享庫,而不想重新編譯整個程序時,怎麼辦呢?上例已經做了提前安排,就是lib2裏也有一個同名的共享庫,且函數接口也相同,只是函數內容不同。可以把lib2裏的函數拷貝到lib1裏,覆蓋原先的庫函數,看看執行結果:
hejinxin@google:~/Mytest/project/lib1$ ls
libsay.so.bak  say1.a  say1.c  say1.o
hejinxin@google:~/Mytest/project/lib1$ cp ../lib2/libsay.so  .
hejinxin@google:~/Mytest/project/lib1$ ls
libsay.so  libsay.so.bak  say1.a  say1.c  say1.o
hejinxin@google:~/Mytest/project/lib1$ cd ..
hejinxin@google:~/Mytest/project$ ls
lib1  lib2  test  test.c
hejinxin@google:~/Mytest/project$ ./test
Say2! 
結果輸出的是Say2!,說明共享庫更新成功!


1、什麼是共享庫

    共享庫的代碼是可以在多個應用程序之間共享的,也就是如果有多個程序(或者叫進程)調用相同的函數,可以把這些共同的函數提取出來製作成共享庫。這樣在運行時,內存中只需要拷貝一份就可以了。

2、共享庫與靜態庫的區別

    靜態庫就是目標文件的簡單打包,在編譯過程中調用靜態庫,就會將涉及到的目標文件拷貝進可執行文件中,然後鏈接程序進行重定位。此後,程序的運行、調試都不再需要靜態庫了。如果每個可執行文件都拷貝一些基礎函數或者目標文件,會有嚴重的內存空間浪費。同時,共享庫在軟件更新時也有優勢,只要保證接口不變,用新的同名共享庫覆蓋原有的庫就實現了軟件更新。

3、如何編譯共享庫

    編譯共享庫需要給gcc指定相關的設置,-fPIC -shared,PIC是position independent code的縮寫,就是位置無關的代碼;-shared表明代碼是共享的。

4、鏈接過程是怎樣的

    程序編譯的最後一個過程就是鏈接,鏈接器ld負責將多個目標文件鏈接起來,修改它們中的重定位表,使得可重定位符號指定到特定位置。

5、運行時如何操作

    在程序運行時,系統會首先判斷這個程序是動態鏈接的還是靜態鏈接的。如果是動態鏈接的,運行時動態連接器ld-linux.so檢查程序依賴的共享庫,並判斷是否已經在內存中?如果不在,加載這些庫綁定重定位的符號。這裏就有一個問題了,運行時動態連接器是如何找到共享庫?會首先查找/etc/ld.so.cache,如果沒有這個庫的路徑,就程序就無法運行。而ld.so.cache其實是通過ldconfig程序讀取/etc/ld.so.conf文件的內容,因此,安裝庫後應該把庫文件的路徑加入到/etc/ld.so.conf文件裏面。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章