1、指出static library 與 shared library的區別
static library 與 shared library的共同點在于它們都是library,都是編譯完成的二進制代碼,可供其他程序調用。
對于static library , 當程序需要使用它時 , 編譯器會將 static library 與程序結合在一起,當結合完成后,程序本身就包含了library的所有內容,故即使將static library刪除也可以使用程序。
對于shared library,其與調用其的程序為動態鏈接關系,調用它的程序并不將庫代碼包含在內,而是在運行時調用。這樣一來生成的程序體積就比static library要小,而如果刪除shared library,程序將無法運行。
static犧牲了體積換取了更高的性能和程序的獨立性,而shared犧牲了一些性能換取了體積,這兩種方法沒有好壞之分,只看應用場景的需求。
2、研究創建static library 與 shared library的方法
我們可以使用gcc來創建shared library,并將其與需要調用它的程序進行鏈接。
首先,我們編寫一個test2.c,其包含一個print
函數:
#include <stdio.h>
void print();
void print()
{
printf("HW\n");
}
然后,我們編寫一個test.c,其中包含一個main
函數,調用print
函數:
#include <stdio.h>
int main(void)
{
print();
return 0;
}
有了這些準備,我們可以開始把test.c
和test2.c
編譯成二進制目標文件,并且最終連接成為可執行文件了:
gcc -c -Wall -Werror -fPIC test2.c
gcc -shared test2.o
mv a.out libprint.so
gcc -Wall test.c -lprint -L.
對于上述代碼,作出詳細解釋:
首先,gcc -c -Wall -Werror -fPIC test2.c
,其中的-c
參數告訴gcc要做的是"Compile and assemble, but do not link",也就是生成沒有連接庫的二進制目標文件,-Wall
與-Werror
是告訴gcc產生所有警告并且將警告作為錯誤來對待,這是為了讓gcc以最嚴格的方式來檢查我們的代碼,最后的-fPIC
是讓gcc去“Generate position-independent code”
關于這個"position-independent code",引用維基百科上的一段解釋:
In computing, position-independent code (PIC) or position-independent executable (PIE) is a body of machine code that, being placed somewhere in the primary memory, executes properly regardless of its absolute address.
這樣的代碼在shared library中十分常用,因為它們可以被復用,例如,當一個需要glibc的程序將glibc加載到內存中后,其他程序也可以使用這份數據,而不需要重新加載一次,這優化了性能,減小了內存消耗。
gcc -shared test2.o
是告訴gcc去生成shared library代碼,也就是我們需要的東西,mv a.out libprint.so
語句將生成的shared library從a.out
重命名為libprint.so
最后,我們用gcc -Wall test.c -lprint -L.
編譯test.c源代碼,用-lprint
指明需要連接的庫文件是libprint.so
,而-L.
指明了libprint.so
就在當前文件夾下
接下來我們來試著創建一個static library,依然使用我們上面編寫的test.c
和test2.c
,由于很多gcc參數已在上文解釋過,在這里不再重復解釋:
gcc -c -Wall -Werror test2.c #注意這里與上文稍有不同,去掉了-fPIC參數,因為我們要創建的是靜態庫,不需要position-independent code
ar rcs libprint.a test2.o #解釋看下文
gcc -static test.c -L. -lprint #-static參數阻止gcc連接動態庫
這里重點解釋一下ar
語句,這句語句在各種教你編譯靜態庫的文章中都有出現,ar
官方manual中介紹ar是用來create, modify, and extract from archives的工具,于是我便覺得非常奇怪,這個打包文件的工具為什么需要出現在這里來制作靜態庫?
經過查找,找到了這段話:
Unix linkers, usually invoked through the C compiler cc, can read ar files and extract object files from them
看樣子這里的ar命令只是為了打包多個靜態庫,給編譯提供方便用的,所以,我認為,我們這里只使用一個靜態庫,用ar命令沒有必要,可以直接:
gcc -static test.c test2.o
,經過測試,這種做法確實可行
3、寫一個你自己的shared library
4、寫一個你自己的static library
5、調用你自己寫的上述庫
已在第二問回答中回答,略過
6、在探究過程這種提出一個新問題并自行研究解決
問題:
在將我們的主程序test.c與編譯好的shared library鏈接的時候,我們用的是:gcc -Wall test.c -lprint -L.
,那么,gcc是如何完成這個動作的呢?是否調用了其他的什么工具?
答案:
調用的是ld
,具體的調用參數可以用gcc -v
看到,如果想要用ld
來手動進行鏈接,需要徹底了解glibc的結構,這個沒有必要而且也不太現實,這里做了一點能說明問題的小實驗:
gcc -static -c test2.c #生成test2.o,我們要的static library
gcc -c test.c # 生成test.o,我們接下來要把這兩個文件用ld鏈接起來
之后,我們來試著使用一下ld
:
% ld test.o
ld: warning: cannot find entry symbol _start; defaulting to 00000000004000b0
test.o: In function `main':
test.c:(.text+0xa): undefined reference to `print'
可以看到,如果直接讓ld鏈接test.o,它會提示找不到print
函數,這個是我們在test2.o
這個static library中提供的,而:
ld test.o test2.o
ld: warning: cannot find entry symbol _start; defaulting to 00000000004000b0
test2.o: In function `print':
test2.c:(.text+0xc): undefined reference to `puts'
當我們讓ld
把test.o
與test2.o
鏈接時,它又提示找不到puts
函數,這個很明顯是由glibc提供的函數,所以,為了用ld
手動解決問題,我們還需要知道glibc的具體結構,這個是沒有必要的
完