【Linux技術】linux庫文件編寫·入門

1、爲何要使用庫文件

  咱們在實際編程中確定會遇到這種狀況:有幾個項目裏有一些函數模塊的功能相同,實現代碼也相同,也是咱們所說的重複代碼。好比,不少項目裏都有一個用戶驗證的功能。
  代碼段以下:編程

//UserLogin.h文件,提供函數聲明
int IsValidUser(char* username, int namelen); //UserLogin.c文件,實現對用戶信息的驗證
int IsValidUser(char* username, int namelen) {   int IsValid = 0;   /*下面是具體的處理代碼,略去*/
  return IsValid }
View Code

  若是每一個項目都保存着這兩個UserLogin.h和UserLogin.c文件,會有如下幾個弊端:緩存

  1. 每一個項目裏都有重複的模塊,形成代碼重複。
  2. 代碼的重用性很差,一旦IsValidUser的代碼發生了變化,爲了保持設計的一致性,咱們還要手工修改其餘項目裏的UserLogin.c文件,既費時又費力,還容易出錯。庫文件就是對公共代碼的一種組織形式。

  爲了解決上面兩個弊端,就提出了用庫文件存放公共代碼的解決方案,其要點就是把公共的(也就是能夠被屢次複用的)目標代碼從項目中分離出來,統一存放到庫文件中,項目要用到這些代碼的時候,在編譯或者運行的時候從庫文件中取得目標代碼便可。庫文件又分兩種:靜態庫和動態庫。ide

2、靜態庫與動態庫

  若是程序是在編譯時加載庫文件的,就是使用了靜態庫。若是是在運行時加載目標代碼,就成爲動態庫。換句話說,若是是使用靜態庫,則靜態庫代碼在編譯時就拷貝到了程序的代碼段,程序的體積會膨脹。若是使用動態庫,則程序中只保留庫文件的名字和函數名,在運行時去查找庫文件和函數體,程序的體積基本變化不大。函數

  • 靜態庫的原則是「以空間換時間」,增長程序體積,減小運行時間;
  • 動態庫則是「以時間換空間」,增長了運行時間,但減小了程序自己的體積。

 


3、靜態庫的編寫和使用

一、概述

  靜態庫文件的擴展名通常爲.a,其編寫步驟很簡單。工具

  1. 編寫函數代碼
  2. 編譯生成各目標文件
  3. 用ar文件對目標文件歸檔,生成靜態庫文件。

  注意歸檔文件名必須以lib打頭。測試

  使用要點:spa

  1. 在gcc 的-I參數後加上靜態庫頭文件的路徑。
  2. 在gcc 的-L參數後加上庫文件所在目錄
  3. 在gcc 的-l參數後加上庫文件名,可是要去掉lib和.a擴展名。

  好比庫文件名是libtest.a 那麼參數就是 -l test設計

 

二、編寫最簡單的靜態庫文件

  編寫以下兩個文件,注意放在同一目錄中指針

  • lcw_lib.h   //靜態庫頭文件
  • lcw_lib.c   //靜態庫實現文件
//lcw_lib.h 文件的內容
void test(); //lcw_lib.c 文件的內容
#inlcude <stdio.h>
void test() {   printf("lcw_lib test ! \n"); }
View Code

 


三、製做庫文件

⑴生成目標文件

mystery@lcw:~/Desktop/code/lib_test$ gcc -c lcw_lib.c mystery@lcw:~/Desktop/code/lib_test$ ls lcw_lib.c lcw_lib.h lcw_lib.o

  執行完後會生成一個lcw_lib.o文件code

⑵用ar命令歸檔,格式爲ar -rc <生成的檔案文件名> <.o文件名列表>

  再次提醒,歸檔文件名必定要以lib打頭, .a結尾。

mystery@lcw:~/Desktop/code/lib_test$ ar -rc libtest.a lcw_lib.o mystery@lcw:~/Desktop/code/lib_test$ ls lcw_lib.c lcw_lib.h lcw_lib.o libtest.a mystery@lcw:~/Desktop/code/lib_test$ 

   執行完後會生成一個libtest.a文件

 


四、使用庫文件

⑴編寫一個測試程序main.c

//main.c 測試靜態庫調用的程序
#include "lcw_lib.h"   //要把函數的頭文件包含進來,不然編譯時會報錯
int main(int argc,char* argv[]) {   test();   return 0; }
View Code

⑵編譯目標文件,注意要把靜態庫頭文件的路徑加到-I參數裏面

mystery@lcw:~/Desktop/code/lib_test$ gcc  -I `pwd` -o main.o -c main.c mystery@lcw:~/Desktop/code/lib_test$ ls lcw_lib.c lcw_lib.h lcw_lib.o libtest.a main.c main.o mystery@lcw:~/Desktop/code/lib_test$ 

  如今生成了一個main.o文件.

⑶生成可執行文件,注意要把靜態庫文件的路徑加到-L參數裏面,把庫文件名(去掉打頭的lib和結尾的.a)加到-l參數後面。

mystery@lcw:~/Desktop/code/lib_test$ gcc -o main -L `pwd` main.o -l test mystery@lcw:~/Desktop/code/lib_test$ ls lcw_lib.c lcw_lib.h lcw_lib.o libtest.a main main.c main.o mystery@lcw:~/Desktop/code/lib_test$ 

  此時就會生成一個名爲main的可執行文件.

⑷執行可執行文件查看效果

  執行./main

mystery@lcw:~/Desktop/code/lib_test$ ./main lcw_lib test ! mystery@lcw:~/Desktop/code/lib_test$ 

  OK,測試成功。

 


4、動態庫的編寫

一、概述

  動態庫通常以.so結尾,就是shared object的意思.
  生成步驟

  1. 編寫函數代碼
  2. 編譯生成動態庫文件,要加上 -shared 和 -fpic 選項 ,庫文件名以lib開頭, 以.so 結尾。

  使用方式分爲兩種: 隱式調用和顯示調用

  1. 隱式調用相似於靜態庫的使用,但需修改動態連接庫的配置文件/etc/ld.so.conf;
  2. 顯示調用則是在主程序裏使用dlopen、dlsym、dlerror、dlclose等系統函數。

二、編寫最簡單的動態庫文件

  爲了便於對照, 現仍然採用靜態庫中的文件作例子.

mystery@lcw:~/Desktop/code/lib_test$ ls lcw_lib.c lcw_lib.h lcw_lib.o libtest.a main main.c main.o mystery@lcw:~/Desktop/code/lib_test$ rm main.o main libtest.a lcw_lib.o mystery@lcw:~/Desktop/code/lib_test$ ls lcw_lib.c lcw_lib.h main.c mystery@lcw:~/Desktop/code/lib_test$ 

 

三、編譯生成動態庫 ,庫文件名以lib開頭, 以.so 結尾。

mystery@lcw:~/Desktop/code/lib_test$ gcc -fpic -shared -o libtest.so lcw_lib.c mystery@lcw:~/Desktop/code/lib_test$ ls lcw_lib.c lcw_lib.h libtest.so main.c mystery@lcw:~/Desktop/code/lib_test$ 

  此時就生成一個libtest.so文件。

 


5、動態庫的隱式調用

  隱式調用的含義是代碼裏不出現庫文件名,就是說這個代碼和調用靜態庫的代碼是相似的。

一、編寫測試文件

  //main.c 測試動態庫隱式調用的程序,與前面的同樣

二、 編譯測試程序,與靜態庫相似,要把頭文件的路徑加到-I參數裏面

mystery@lcw:~/Desktop/code/lib_test$ gcc -I `pwd` -o main.o -c main.c mystery@lcw:~/Desktop/code/lib_test$ ls lcw_lib.c lcw_lib.h libtest.so main.c main.o mystery@lcw:~/Desktop/code/lib_test$ 

  如今生成了一個main.o文件。

三、鏈接生成測試程序

mystery@lcw:~/Desktop/code/lib_test$ gcc -o main -L `pwd` main.o -l test mystery@lcw:~/Desktop/code/lib_test$ ls lcw_lib.c lcw_lib.h libtest.so main main.c main.o mystery@lcw:~/Desktop/code/lib_test$ 

  如今生成了一個main文件。

四、執行測試程序

mystery@lcw:~/Desktop/code/lib_test$ ./main ./main: error while loading shared libraries: libtest.so: cannot open shared object file: No such file or directory mystery@lcw:~/Desktop/code/lib_test$ 

  出現錯誤,這個緣由就是程序運行時並不知道動態庫所在的路徑,所以天然找不到。解決方法以下。

 


6、使動態庫被系統共享的三種辦法

  (1)拷貝動態連接庫到系統共享目錄下,或在系統共享目錄下爲該動態連接庫創建鏈接(硬鏈接或符號鏈接都可,經常使用符號鏈接).這裏說的系統共享目錄,指的是LINUX動態連接庫存放的目錄,包括/lib,/usr/lib以及/etc/ld.so.conf文件內所列的一系列目錄.

實例:執行

# cp libtest.so /lib # ldconfig 或: # ln -s `pwd`/libtest.so /lib # ldconfig

注意pwd先後有兩個反引號`,其目的是取得pwd命令的輸出,即當前目錄.此時再執行main,便可成功.

  (2)將動態連接庫所在目錄名追加到動態連接庫配置文件/etc/ld.so.conf中.

# pwd >> /etc/ld.so.conf # ldconfig

 

此時再執行main,便可成功.

  (3)利用動態連接庫管理命令ldconfig,強制其搜索指定目錄,並更新緩存文件,便於動態裝入.  

# ldconfig `pwd`

此時再執行main,便可成功.

  要注意,第三種方法雖然有效,但效果是暫時的,供程序測試還能夠,一旦再度運行ldconfig,則緩存文件內容可能改變,所需的動態連接庫可能不被系統共享了.

  不管哪一種辦法,其實質都是用ldconfig命令把動態庫文件所在路徑加入到系統庫列表中,(前兩種永久,第三種臨時)

 


7、動態庫的顯式調用

  顯式調用的含義是代碼出現庫文件名,用戶須要本身去打開和管理庫文件。其要點爲:

  1. 把dlfcn.h系統頭文件包含進來
  2. 用dlopen函數打開庫文件,並指定打開方式

  dllope的的第一個參數爲共享庫的名稱,將會在下面位置查找指定的共享庫。

  • 環境變量LD_LIBRARY_PATH列出的用分號間隔的全部目錄。
  • 文件/etc/ld.so.cache中找到的庫的列表,由ldconfig命令刷新。
  • 目錄usr/lib。
  • 目錄/lib。
  • 當前目錄。

  第二個參數爲打開共享庫的方式。有兩個取值

  1. RTLD_NOW:將共享庫中的全部函數加載到內存
  2. RTLD_LAZY:會推後共享庫中的函數的加載操做,直到調用dlsym()時方加載某函數
  3. 用dlerror()函數測試是否打開成功,並進行錯誤處理;
  4. 用dlsym得到函數地址,存放在一個函數指針中
  5. 用得到的函數指針進行函數調用。
  6. 程序結束時用dlclose關閉打開的動態庫,防止資源泄露。
  7. 用ldconfig工具把動態庫的路徑加到系統庫列表中

一、編寫測試文件

  //main.c 測試動態庫顯式調用的程序

 1 #include<dlfcn.h>      //用於動態庫管理的系統頭文件
 2  #include "lcw_lib.h"    //要把函數的頭文件包含進來,不然編譯時會報錯
 3  int main(int argc,char* argv[])  4  {  5      //聲明對應的函數的函數指針
 6      void (*pTest)();  7      //加載動態庫
 8      void *pdlHandle = dlopen("libtest.so", RTLD_LAZY);  9      //錯誤處理
10      if(pdlHandle == NULL ) 11  { 12          printf("Failed load library\n"); 13          return -1; 14  } 15      char* pszErr = dlerror(); 16      if(pszErr != NULL) 17  { 18          printf("%s\n", pszErr); 19          return -1; 20  } 21      //獲取函數的地址
22      pTest = dlsym(pdlHandle, "test"); 23      pszErr = dlerror(); 24      if(pszErr != NULL) 25  { 26          printf("%s\n", pszErr); 27  dlclose(pdlHandle); 28          return -1; 29  } 30      //實現函數調用
31      (*pTest)(); 32      //程序結束時關閉動態庫
33  dlclose(pdlHandle); 34      return 0; 35  }
View Code

 

二、編譯測試文件 使用-ldl選項指明生成的對象模塊須要使用共享庫

mystery@lcw:~/Desktop/code/lib_test$ gcc -o main main.c -ldl mystery@lcw:~/Desktop/code/lib_test$ ls lcw_lib.c lcw_lib.h libtest.so main main.c mystery@lcw:~/Desktop/code/lib_test$ 

  執行完後就生成了一個main文件,i注意參數 -ldl 須要放在最後,放在前面時編譯沒法經過

相關文章
相關標籤/搜索