1. 程式人生 > >linux動態庫及靜態庫的製作 和makefile 的簡單編寫

linux動態庫及靜態庫的製作 和makefile 的簡單編寫

一.庫

什麼是庫,簡單的可以說是可執行程式碼的二進位制形式,能夠被作業系統載入記憶體執行。作業系統的不同,二者的庫也是不相容的,如windows與linux.

庫又分為靜態庫和動態庫,動態庫又稱為共享庫。linux下靜態庫(.a)檔案,動態庫(.so)檔案。主要存放函式庫的路徑有:/lib , /usr/lib.

二.靜態庫與動態庫

1.靜態庫

這類庫的名字一般是libname.a.利用靜態庫編寫的檔案比較大,原因是整個函式庫中的資料都被整合進目的碼檔案中去。它的優點是,編譯後的執行程式不需要外部的函式庫支援,因為所有使用的函式都已經被編譯進可執行檔案了。同樣它的不足,如果靜態函式庫改變了,那麼你的程式必須重新編譯,而且體積也較大。

2.動態庫

名字一般是libname.so.相對於靜態函式庫,動態函式庫在編譯的時候並沒有被編譯進目的碼中,你的程式執行到相關函式是菜呼叫函式庫裡的函式,因此動態函式庫所產生的可執行檔案比較小。由於函式庫沒有被整合進你的程式,而是程式執行時動態申請並呼叫,所以程式的執行環境中必須提供相應的庫。動態函式庫的改變並不影響你的程式,所以動態函式庫的升級比較方便。而且如果多個應用程式都要使用同一函式庫,動態庫就非常適合,可以減少應用程式的體積。

三.庫的建立

靜態庫的建立

gcc -c filen.c

ar -cr libname.a file1.o file2.o 。。

ar:靜態函式庫建立的命令-c :create的意思-r :replace的意思,表示當前插入的模組名已經在庫中存在,則替換同名的模組。如果若干模組中有一個模組在庫中不存在,ar顯示一個錯誤資訊,並不替換其他同名的模組。預設的情況下,新的成員增加在庫德結尾處。

動態庫的建立

gcc -shard -fpic -o libname.so test1.c test2.c ....

-fpic:產生程式碼位置無關程式碼

-shared 生成一個共享庫

四.實際操作

1.編寫三個簡單函式ADD.c SUB.c MUL.c如下

ADD.c

 #include <stdio.h>
 int ADD(int a, int b)
  {
      return a+b;
  }


 SUB.c

#include <stdio.h>
  int SUB(int a ,int b)
  {
      return a-b;
  }


MUL.c

 #include <stdio.h>
  int MUL(int a , int b)
  
  {
      return a*b;
  }
 和一個頭檔案 fun.h

#ifdef __FUN_H_
#define __FUN_H_
  extern ADD(int a , int b);
  extern SUB(int a , int b);
  extern MUL(int a , int b);
#endif
以及一個main函式main.c

main.c

#include <stdio.h>

 #include <fun.h>
 int main (void)
{
     int a=5;
     int b=10;
     printf("a=5,b=10\n");
     printf("a+b=%d\n",ADD(a,b));
     printf("a-b=%d\n",SUB(a,b));
     printf("a*b=%d\n",MUL(a,b));
     return 0;
 }
 其中main.c 放在~/app下 fun.h ADD.c SUB.c MUL.c放在~/src下

2.生成靜態庫

[[email protected] src]$ ls
ADD.c  fun.h  makefile  MUL.c  SUB.c
[[email protected] src]$ gcc -c *.c
[[email protected] src]$ ls
ADD.c  ADD.o  fun.h  makefile  MUL.c  MUL.o  SUB.c  SUB.o
[[email protected] src]$ ar -cr libfun.a *.o
[[email protected] src]$ ls
ADD.c  ADD.o  fun.h  libfun.a  makefile  MUL.c  MUL.o  SUB.c  SUB.o

上中libfun.a即是一個靜態庫

生成動態庫

[[email protected] src]$ ls
ADD.c  ADD.o  fun.h  libfun.a  makefile  MUL.c  MUL.o  SUB.c  SUB.o
[[email protected] src]$ gcc -shared -fpic -o libfun.so  *.c  
[[email protected] src]$ ls
ADD.c  ADD.o  fun.h  libfun.a  libfun.so  makefile  MUL.c  MUL.o  SUB.c  SUB.o

上中libfun.so 就是一個動態庫

3.靜態庫與動態庫的使用

對main.c進行編譯

[[email protected] app]$ ls
main.c  makefile
[[email protected] app]$ gcc main.c
main.c:14:17: 錯誤:fun.h:沒有那個檔案或目錄


編譯器報錯 找不到標頭檔案  需要指定標頭檔案的位置 

-I選項 指定標頭檔案的路徑

[[email protected] app]$ gcc -I../src main.c
/tmp/ccZw9v3Q.o: In function `main':
main.c:(.text+0x30): undefined reference to `ADD'
main.c:(.text+0x5a): undefined reference to `SUB'
main.c:(.text+0x84): undefined reference to `MUL'
collect2: ld 返回 1    

  連結器再一次報錯  未能找庫

-L選項來指定庫的路徑

-l選項來指定庫的名字 (去掉lib和.a .so)剩下的部分  即-lfun

[[email protected] app]$ gcc -I../src main.c -L../src -lfun -o app
[[email protected] app]$ ls
app  main.c  makefile

生成一個可執行檔案 app

[[email protected] app]$ ./app
./app: error while loading shared libraries: libfun.so: cannot open shared object file: No such file or directory

執行時出錯  找不到動態庫

[[email protected] app]$ file app
app: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, not stripped

這時我們可以發現 系統編譯時預設使用的是動態連結  執行時我們需要指定動態庫的位置

兩種方法:

1:將函式庫移動到/lib 或者/usr/lib下 (需要root許可權)

2:修改一個環境變數 LD_LIBRARY_PATH

在這裡我採用第二種方法

[[email protected] app]$ ./app
./app: error while loading shared libraries: libfun.so: cannot open shared object file: No such file or directory
[[email protected] app]$ file app
app: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, not stripped
[[email protected] app]$ cd ../src
[[email protected] src]$ ls
ADD.c  ADD.o  fun.h  libfun.a  libfun.so  makefile  MUL.c  MUL.o  SUB.c  SUB.o
[[email protected] src]$ pwd
/home/hongfuhao/src
[[email protected] src]$ cd ../app
[[email protected] app]$ ls
app  main.c  makefile
[[email protected] app]$ export LD_LIBRARY_PATH=/home/hongfuhao/src:$LD_LIBRARY_PATH
[[email protected] app]$ ./app
a=5,b=10
a+b=15
a-b=-5
a*b=50

可以看到程式成功執行

如果想採用靜態連結方式  需要-static關鍵字

[[email protected] app]$ ls
main.c  makefile
[[email protected] app]$ gcc -static -I../src main.c -o bpp -L../src -lfun
[[email protected] app]$ ls
bpp  main.c  makefile
[[email protected] app]$ ./bpp
a=5,b=10
a+b=15
a-b=-5
a*b=50

這時再看bpp檔案

[[email protected] app]$ file bpp
bpp: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.18, not stripped

採用的靜態連結執行時不需要指定庫的路徑

兩個檔案app與bpp的大小 使用du命令

[[email protected] app]$ du app
8       app
[[email protected] app]$ du bpp
744     bpp

看一個檔案執行依賴的動態庫

[[email protected] app]$ ldd app
        linux-vdso.so.1 =>  (0x00007ffe7a3bf000)
        libfun.so => /home/hongfuhao/src/libfun.so (0x00007f775018f000)
        libc.so.6 => /lib64/libc.so.6 (0x00000030cee00000)
        /lib64/ld-linux-x86-64.so.2 (0x00000030ce600000)

以上為app依賴的動態庫檔案
[[email protected] app]$ ldd bpp
        不是動態可執行檔案

bpp不是動態連結所以不顯示

上述就完成一個靜態庫和動態庫的建立和使用

五.makefile

1.概述

Linux 環境下的程式設計師如果不會使用GNU make來構建和管理自己的工程,應該不能算是一個合格的專業程式設計師,至少不能稱得上是 Unix程式設計師。在 Linux(unix )環境下使用GNU 的make工具能夠比較容易的構建一個屬於你自己的工程,整個工程的編譯只需要一個命令就可以完成編譯、連線以至於最後的執行。不過這需要投入一些時間去完成一個或者多個稱之為Makefile 檔案的編寫。

所要完成的Makefile 檔案描述了整個工程的編譯、連線等規則。其中包括:工程中的哪些原始檔需要編譯以及如何編譯、需要建立那些庫檔案以及如何建立這些庫檔案、如何最後產生想要得可執行檔案。儘管看起來可能是很複雜的事情,但是為工程編寫Makefile 的好處是能夠使用一行命令來完成“自動化編譯”,一旦提供一個(通常對於一個工程來說會是多個)正確的 Makefile。編譯整個工程你所要做的唯一的一件事就是在shell 提示符下輸入make命令。整個工程完全自動編譯,極大提高了效率。

make是一個命令工具,它解釋Makefile 中的指令(應該說是規則)。在Makefile檔案中描述了整個工程所有檔案的編譯順序、編譯規則。Makefile 有自己的書寫格式、關鍵字、函式。像C 語言有自己的格式、關鍵字和函式一樣。而且在Makefile 中可以使用系統shell所提供的任何命令來完成想要的工作。Makefile(在其它的系統上可能是另外的檔名)在絕大多數的IDE 開發環境中都在使用,已經成為一種工程的編譯方法

2.makefile 小結即規範

Make  執行總目標

Make clean 執行makefile 中的clean目標

Make -C directory directory中執行make

Make clean -C directory  同上兩命令

Make -f common_makefile 通過-f 指定一個makefile檔案

Make var=value   makefie傳一個引數其值為value

註釋

VAR=XXX    定義變數VAR,強制賦值

VAR+=XXX 追加

VAR=XXX   之前定義這用之前  沒有定義 則定義

Target: depend1 depend2 . .          #依賴可以是檔案(目錄)或其他目標

(tab)  action1 action2            #動作那一行必須以TAB鍵打頭

Depend1:

@(tab)Action1 action2    #@j鍵表示不列印該行動作資訊


2.實際操作

編寫自己的makefile

使用之前靜態庫與動態庫的案例

建立一個makefile

  1 LIB_NAME?=fun
  2 
  3 all:static_library shared_library
  4 
  5 static_library:
  6     gcc -c *.c;
  7     ar  -cr lib${LIB_NAME}.a *.o;
  8 
  9 shared_library:
 10     gcc -shared -fpic -o lib${LIB_NAME}.so *.c;
 11 
 12 clean:
 13     rm -rf *.o
 14     rm -rf *.a *.so
 

[[email protected] src]$ ls
ADD.c  fun.h  makefile  MUL.c  SUB.c

makefile已經建立完成  

make命令可以完成之前的操作

[[email protected] src]$ ls
ADD.c  fun.h  makefile  MUL.c  SUB.c
[[email protected] src]$ make
gcc -c *.c;
ar  -cr libfun.a *.o;
gcc -shared -fpic -o libfun.so *.c;
[[email protected] src]$ ls
ADD.c  ADD.o  fun.h  libfun.a  libfun.so  makefile  MUL.c  MUL.o  SUB.c  SUB.o

完成了靜態庫和動態庫的建立  只需要make一下

對main.c進行makefile的編寫

makefile                                                                                                                           
  1 APP_NAME?=APP
  2 
  3 all:lib_make
  4     gcc -static -I../src main.c -L../src -lfun -o ${APP_NAME};
  5 
  6 lib_make:
  7     make -C ../src;
  8 
  9 clean:
 10     rm -rf ${APP_NAME}

[[email protected] app]$ ls
main.c  makefile
[[email protected] app]$ ls
main.c  makefile

只需要make一下 就可以完成main.c編譯

[[email protected] app]$ make
make -C ../src;
make[1]: Entering directory `/home/hongfuhao/src'
gcc -c *.c;
ar  -cr libfun.a *.o;
gcc -shared -fpic -o libfun.so *.c;
make[1]: Leaving directory `/home/hongfuhao/src'
gcc -static -I../src main.c -L../src -lfun -o APP;
[[email protected] app]$ ls
APP  main.c  makefile

生成一個APP的可執行檔案makefile中執行的是靜態連結

可以直接執行APP

[[email protected] app]$ ./APP
a=5,b=10
a+b=15
a-b=-5
a*b=50

程式成功執行

即完成了一個簡單的makefile的編寫