歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux教程 >> Linux 中的靜態庫和動態庫簡介及生成過程示例

Linux 中的靜態庫和動態庫簡介及生成過程示例

日期:2017/2/28 13:56:44   编辑:Linux教程

在實際的軟件開發項目中,不是每一行代碼都需要我們親自寫。在我們的軟件產品中,有一些代碼(尤其是函數)的出現頻率很高,它們可以被當作公共代碼來反復使用。為了避免重復勞動,我們就把這些公共代碼編譯為庫文件,供需要的程序調用。在Linux中,庫分為靜態庫和動態庫兩種。

本文對靜態庫和動態庫進行了詳細的介紹,並用實際的C代碼演示了這兩種庫的生成過程。

一、靜態庫和動態庫簡介

眾所周知,程序一般需要經過預處理、編譯、匯編和鏈接這幾個步驟才能變成可執行的程序。在實際的軟件開發中,對於一些需要被許多模塊反復使用的公共代碼,我們就將它們編譯為庫文件。

庫是一種可執行代碼的二進制形式,可以被操作系統載入內存執行。Linux支持的庫分為靜態庫和動態庫,動態庫又稱共享庫。一般說來,Linux中的一些重要的庫是存放在lib目錄下的。

靜態庫文件的後綴為.a,在Linux下一般命名為libxxx.a。在鏈接步驟中,連接器將從靜態庫文件中取得所需的代碼,復制到生成的可執行文件中。因此,整個庫中的所有函數都被編譯進了目標代碼中。

動態庫文件的後綴為.so,在Linux下一般命名為libxxx.so。相對於靜態庫,動態庫在編譯的時候並沒有被編譯進目標代碼中,而是程序執行到相關函數時才調用庫中對應的函數。

可以看到,靜態庫的優點是編譯後的執行程序不需要外部的函數庫支持,缺點是如果靜態函數庫改變了,那麼你的程序必須重新編譯;而動態庫在多個應用程序都要使用同一函數庫的時候就非常適合,但前提是程序的運行環境中必須提供相應的庫。
不管是靜態庫,還是動態庫,都是由*.o目標文件生成的。

二、靜態庫生成示例

1.單個文件生成靜態庫示例

我們編寫如下簡單的三個程序文件:test.h、test.c和main.c,在main.c中要調用test.c中實現的函數test。
test.h文件內容:

  1. #include<stdio.h>
  2. voidtest();

test.c文件內容:

  1. #include"test.h"
  2. voidtest()
  3. {
  4. printf("this is in test......\n");
  5. }

main.c文件內容:

  1. #include"test.h"
  2. int main()
  3. {
  4. test();
  5. return0;
  6. }

將此三個文件上傳到Linux機器上,編譯生成靜態庫文件,之後調用庫文件的整個過程如下所示:

  1. ~/zhouzhaoxiong/zzx/mytest/a/single> ll
  2. -rw-------1 zhou dba 53Nov 416:04 main.c
  3. -rw-------1 zhou dba 80Nov 416:04test.c
  4. -rw-------1 zhou dba 36Nov 416:04test.h
  5. ~/zhouzhaoxiong/zzx/mytest/a/single> gcc-c test.c
  6. ~/zhouzhaoxiong/zzx/mytest/a/single> ll
  7. -rw-------1 zhou dba 53Nov 416:04 main.c
  8. -rw-------1 zhou dba 80Nov 416:04test.c
  9. -rw-------1 zhou dba 36Nov 416:04test.h
  10. -rw-rw-rw-1 zhou dba 1624Nov 416:06test.o
  11. ~/zhouzhaoxiong/zzx/mytest/a/single> ar -r libtest.a test.o
  12. ar: creating libtest.a
  13. ~/zhouzhaoxiong/zzx/mytest/a/single> ll
  14. -rw-------1 zhou dba 53Nov 416:04 main.c
  15. -rw-rw-rw-1 zhou dba 1766Nov 416:06 libtest.a
  16. -rw-------1 zhou dba 80Nov 416:04test.c
  17. -rw-------1 zhou dba 36Nov 416:04test.h
  18. -rw-rw-rw-1 zhou dba 1624Nov 416:06test.o
  19. ~/zhouzhaoxiong/zzx/mytest/a/single> gcc-o test main.c libtest.a
  20. ~/zhouzhaoxiong/zzx/mytest/a/single> ll
  21. -rw-------1 zhou dba 52Nov 416:09 main.c
  22. -rwxrwxrwx 1 zhou dba 11876Nov 416:09test
  23. -rw-rw-rw-1 zhou dba 1766Nov 416:06 libtest.a
  24. -rw-------1 zhou dba 80Nov 416:04test.c
  25. -rw-------1 zhou dba 36Nov 416:04test.h
  26. -rw-rw-rw-1 zhou dba 1624Nov 416:06test.o
  27. ~/zhouzhaoxiong/zzx/mytest/a/single> ./test
  28. thisisintest......

我們可以看到,生成庫文件的命令是“ar -r libtest.a test.o”,而將靜態庫文件編譯進代碼的命令是“gcc -o test main.c libtest.a”。

這樣生成了靜態庫文件libtest.a之後,如果還有其他程序要調用test.c中實現的函數,只需要將test.h和libtest.a拷貝到對應的代碼工程中,然後執行類似“gcc -o test main.c libtest.a”這樣的命令即可。

2.多個文件生成靜態庫示例

我們編寫如下簡單的五個程序文件:test.h、test_1.c、test_2.c、test_3.c和main.c,在main.c中要調用test_1.c、test_2.c、test_3.c中實現的函數test_1、test_2、test_3。

test.h文件內容:

  1. #include<stdio.h>
  2. void test_1();
  3. void test_2();
  4. void test_3();

test_1.c文件內容:

  1. #include"test.h"
  2. void test_1()
  3. {
  4. printf("this is in test_1......\n");
  5. }

test_2.c文件內容:

  1. #include"test.h"
  2. void test_2()
  3. {
  4. printf("this is in test_2......\n");
  5. }

test_3.c文件內容:

  1. #include"test.h"
  2. void test_3()
  3. {
  4. printf("this is in test_3......\n");
  5. }

main.c文件內容:

  1. #include"test.h"
  2. int main()
  3. {
  4. test_1();
  5. test_2();
  6. test_3();
  7. return0;
  8. }

將此五個文件上傳到Linux機器上,編譯生成靜態庫文件,之後調用庫文件的整個過程如下所示:

  1. ~/zhouzhaoxiong/zzx/mytest/a/more> ll
  2. -rw-------1 zxin10 dba 96Nov 416:11 main.c
  3. -rw-------1 zxin10 dba 70Nov 416:04test.h
  4. -rw-------1 zxin10 dba 84Nov 416:04 test_1.c
  5. -rw-------1 zxin10 dba 84Nov 416:04 test_2.c
  6. -rw-------1 zxin10 dba 84Nov 416:04 test_3.c
  7. ~/zhouzhaoxiong/zzx/mytest/a/more> gcc-c test_1.c test_2.c test_3.c
  8. ~/zhouzhaoxiong/zzx/mytest/a/more> ll
  9. -rw-------1 zxin10 dba 96Nov 416:11 main.c
  10. -rw-------1 zxin10 dba 70Nov 416:04test.h
  11. -rw-------1 zxin10 dba 84Nov 416:04 test_1.c
  12. -rw-rw-rw-1 zxin10 dba 1624Nov 416:15 test_1.o
  13. -rw-------1 zxin10 dba 84Nov 416:04 test_2.c
  14. -rw-rw-rw-1 zxin10 dba 1624Nov 416:15 test_2.o
  15. -rw-------1 zxin10 dba 84Nov 416:04 test_3.c
  16. -rw-rw-rw-1 zxin10 dba 1624Nov 416:15 test_3.o
  17. ~/zhouzhaoxiong/zzx/mytest/a/more> ar -r libtest.a test_1.o test_2.o test_3.o
  18. ar: creating libtest.a
  19. ~/zhouzhaoxiong/zzx/mytest/a/more> ll
  20. -rw-------1 zxin10 dba 96Nov 416:11 main.c
  21. -rw-rw-rw-1 zxin10 dba 5158Nov 416:15 libtest.a
  22. -rw-------1 zxin10 dba 70Nov 416:04test.h
  23. -rw-------1 zxin10 dba 84Nov 416:04 test_1.c
  24. -rw-rw-rw-1 zxin10 dba 1624Nov 416:15 test_1.o
  25. -rw-------1 zxin10 dba 84Nov 416:04 test_2.c
  26. -rw-rw-rw-1 zxin10 dba 1624Nov 416:15 test_2.o
  27. -rw-------1 zxin10 dba 84Nov 416:04 test_3.c
  28. -rw-rw-rw-1 zxin10 dba 1624Nov 416:15 test_3.o
  29. ~/zhouzhaoxiong/zzx/mytest/a/more> gcc-o test main.c libtest.a
  30. ~/zhouzhaoxiong/zzx/mytest/a/more> ll
  31. -rw-------1 zxin10 dba 96Nov 416:11 main.c
  32. -rwxrwxrwx 1 zxin10 dba 12008Nov 416:16test
  33. -rw-rw-rw-1 zxin10 dba 5158Nov 416:15 libtest.a
  34. -rw-------1 zxin10 dba 70Nov 416:04test.h
  35. -rw-------1 zxin10 dba 84Nov 416:04 test_1.c
  36. -rw-rw-rw-1 zxin10 dba 1624Nov 416:15 test_1.o
  37. -rw-------1 zxin10 dba 84Nov 416:04 test_2.c
  38. -rw-rw-rw-1 zxin10 dba 1624Nov 416:15 test_2.o
  39. -rw-------1 zxin10 dba 84Nov 416:04 test_3.c
  40. -rw-rw-rw-1 zxin10 dba 1624Nov 416:15 test_3.o
  41. ~/zhouzhaoxiong/zzx/mytest/a/more>./test
  42. thisisin test_1......
  43. thisisin test_2......
  44. thisisin test_3......

我們可以看到,生成靜態庫文件的命令是“ar -r libtest.a test_1.o test_2.o test_3.o”,而將靜態庫文件編譯進代碼的命令是“gcc -o test main.c libtest.a”。

這樣生成了靜態庫文件libtest.a之後,如果還有其他程序要調用test_1.c、test_2.c、test_3.c中實現的函數,只需要將test.h和libtest.a拷貝到對應的代碼工程中,然後執行類似“gcc -o test main.c libtest.a”這樣的命令即可。

三、動態庫生成示例

1.單個文件生成動態庫示例

我們編寫如下簡單的三個程序文件:so_test.h、test_a.c和test.c,在test.c中要調用test_a.c中實現的函數test_a。
so_test.h文件內容:

  1. #include<stdio.h>
  2. void test_a();

test_a.c文件內容:

  1. #include"so_test.h"
  2. void test_a()
  3. {
  4. printf("this is in test_a...\n");
  5. }

test.c文件內容:

  1. #include"so_test.h"
  2. int main()
  3. {
  4. test_a();
  5. return0;
  6. }

將此三個文件上傳到Linux機器上,編譯生成動態庫文件,之後調用庫文件的整個過程如下所示:

  1. ~/zhouzhaoxiong/zzx/mylib/so> ll
  2. -rw-------1 zxin10 dba 95Nov 417:37 so_test.h
  3. -rw-------1 zxin10 dba 109Nov 417:37test.c
  4. -rw-------1 zxin10 dba 84Nov 410:57 test_a.c
  5. ~/zhouzhaoxiong/zzx/mylib/so> gcc test_a.c -fPIC -shared -o libtest.so
  6. ~/zhouzhaoxiong/zzx/mylib/so> ll
  7. -rwxrwxrwx 1 zxin10 dba 8181Nov 417:43 libtest.so
  8. -rw-------1 zxin10 dba 95Nov 417:37 so_test.h
  9. -rw-------1 zxin10 dba 109Nov 417:37test.c
  10. -rw-------1 zxin10 dba 84Nov 410:57 test_a.c
  11. ~/zhouzhaoxiong/zzx/mylib/so> gcctest.c -L.-ltest -o test
  12. ~/zhouzhaoxiong/zzx/mylib/so> ll
  13. -rwxrwxrwx 1 zxin10 dba 8181Nov 417:43 libtest.so
  14. -rw-------1 zxin10 dba 95Nov 417:37 so_test.h
  15. -rwxrwxrwx 1 zxin10 dba 11805Nov 417:44test
  16. -rw-------1 zxin10 dba 109Nov 417:37test.c
  17. -rw-------1 zxin10 dba 84Nov 410:57 test_a.c
  18. ~/zhouzhaoxiong/zzx/mylib/so>./test
  19. thisisin test_a...

注意,“./test”命令執行成功的前提是在環境變量中添加了.so文件所在的路徑,這個路徑可以在“.bash_profile”文件的“LD_LIBRARY_PATH”變量的值中添加。

我們可以看到,生成動態庫文件的命令是“gcc test_a.c -fPIC -shared -o libtest.so”,而將動態庫文件編譯進代碼的命令是“gcc test.c -L. -ltest -o test”(-L.表示當前路徑)。

這樣生成了動態庫文件libtest.so之後,如果還有其他程序要調用test_a.c中實現的函數,只需要將so_test.h和libtest.so拷貝到對應的代碼工程中,然後執行類似“gcc test.c -L. -ltest -o test”這樣的命令即可(前提是libtest.so所在的路徑在環境變量中設置正確)。

2.多個文件生成動態庫示例

我們編寫如下簡單的五個程序文件:so_test.h、test_a.c、test_b.c、test_c.c和test.c,在test.c中要調用test_a.c、test_b.c、test_c.c中實現的函數test_a、test_b、test_c。

so_test.h文件內容:

  1. #include<stdio.h>
  2. void test_a();
  3. void test_b();
  4. void test_c();

test_a.c文件內容:

  1. #include"so_test.h"
  2. void test_a()
  3. {
  4. printf("this is in test_a...\n");
  5. }

test_b.c文件內容:

  1. #include"so_test.h"
  2. void test_b()
  3. {
  4. printf("this is in test_b...\n");
  5. }

test_c.c文件內容:

  1. #include"so_test.h"
  2. void test_c()
  3. {
  4. printf("this is in test_c...\n");
  5. }

test.c文件內容:

  1. #include"so_test.h"
  2. int main()
  3. {
  4. test_a();
  5. test_b();
  6. test_c();
  7. return0;
  8. }

將此五個文件上傳到Linux機器上,編譯生成動態庫文件,之後調用庫文件的整個過程如下所示:

  1. ~/zhouzhaoxiong/zzx/mylib/test_so> ll
  2. -rwxrwxrwx 1 zxin10 dba 8309Nov 509:12 libtest
  3. -rw-------1 zxin10 dba 70Nov 513:44 so_test.h
  4. -rw-------1 zxin10 dba 105Nov 415:25test.c
  5. -rw-------1 zxin10 dba 84Nov 415:25 test_a.c
  6. -rw-------1 zxin10 dba 84Nov 415:25 test_b.c
  7. -rw-------1 zxin10 dba 84Nov 415:25 test_c.c
  8. ~/zhouzhaoxiong/zzx/mylib/test_so> gcc test_a.c test_b.c test_c.c -fPIC -shared -o libtest.so
  9. ~/zhouzhaoxiong/zzx/mylib/test_so> gcctest.c -L.-ltest -o test
  10. ~/zhouzhaoxiong/zzx/mylib/test_so> ll
  11. -rwxrwxrwx 1 zxin10 dba 8309Nov 513:46 libtest.so
  12. -rw-------1 zxin10 dba 70Nov 513:44 so_test.h
  13. -rwxrwxrwx 1 zxin10 dba 11883Nov 513:46test
  14. -rw-------1 zxin10 dba 105Nov 415:25test.c
  15. -rw-------1 zxin10 dba 84Nov 415:25 test_a.c
  16. -rw-------1 zxin10 dba 84Nov 415:25 test_b.c
  17. -rw-------1 zxin10 dba 84Nov 415:25 test_c.c
  18. ~/zhouzhaoxiong/zzx/mylib/test_so>./test
  19. thisisin test_a...
  20. thisisin test_b...
  21. thisisin test_c...

注意,“./test”命令執行成功的前提仍然是在環境變量中添加了.so文件所在的路徑,這個路徑可以在“.bash_profile”文件的“LD_LIBRARY_PATH”變量的值中添加。

我們可以看到,多個文件生成動態庫文件的命令是“gcc test_a.c test_b.c test_c.c -fPIC -shared -o libtest.so”,而將動態庫文件編譯進代碼的命令是“gcc test.c -L. -ltest -o test”(-L.表示當前路徑)。

這樣生成了動態庫文件libtest.so之後,如果還有其他程序要調用test_a.c、test_b.c、test_c.c中實現的函數,只需要將so_test.h和libtest.so拷貝到對應的代碼工程中,然後執行類似“gcc test.c -L. -ltest -o test”這樣的命令即可(前提是libtest.so所在的路徑在環境變量中設置正確)。

四、總結

有關生成靜態庫和動態庫的命令,說明如下:

第一,在本文中,我們使用的生成靜態庫的命令形如“ar -r test.a test.o”,其中,-r是replace的意思,表示如果當前插入的模塊名已經在庫中存在,則替換同名的模塊。我們也可以用形如“ar -cr test.a test.o”的命令來生成靜態庫,其中-c是create的意思,表示生成。

第二,在本文中,我們使用的生成動態庫文件的命令形如“gcc test_a.c -fPIC -shared -o libtest.so”,其中,fPIC表示編譯為位置獨立的代碼,shared表示生成的庫為共享庫。將動態庫文件編譯進代碼的命令是“gcc test.c -L. -ltest -o test”,-L指定庫查找的位置(注意L後面還有'.'),表示在當前目錄下查找(如果在當前目錄下的lib目錄下查找,可以寫成-L./lib);-l則指定函數庫名,其中的lib和.so省略(如這裡的libtest.so就簡寫為test)。

第三,使用ldd命令可以查看一個可執行程序依賴的共享庫,該命令的使用示例如下所示:

  1. ~/zhouzhaoxiong/zzx/mylib/test_so>lddtest
  2. linux-vdso.so.1=> (0x00007fff1db6e000)
  3. libtest.so =>/home/zhou/lib/libtest.so (0x00007fdbfff21000)
  4. libc.so.6=>/lib64/libc.so.6(0x00007fdbffb95000)
  5. /lib64/ld-linux-x86-64.so.2(0x00007fdc00124000)

可以看到,可執行文件test依賴於四個共享庫,其中libtest.so位於當前用戶的lib目錄下。

Copyright © Linux教程網 All Rights Reserved