Linux靜態庫和動態庫學習總結

簡書成研發表於2014-08-29

一、廢話

之前由於工作需要,要封裝一個Linux加密解密轉換的動態庫,這個之前只做過Windows下面的,Linux下面還真沒有做過,之後做了整一個晚上才算做好,不過其中也學到了不少東西,包括Linux下的動態庫和靜態庫,MakeFile等等。之前就已經寫了一個練習,之後怕又忘了,總結一下備忘,以後也好查。

很大部分內容都是收集的一些東西還有自己學習的體會,有什麼錯誤或者問題請直接提出。

二、關於庫的問題

1.庫的原則

現實中每個程式都要依賴很多基礎的底層庫,不可能每個人的程式碼都從零開始。儘量不重複做別人已經做過的事,就是儘量充分利用別人的勞動成果。就是“站在巨人的肩膀上”做事情。

2.庫的種類

根據連結時期的不同,庫又有:靜態庫和共享庫(動態庫)

二者的不同點在於程式碼被載入的時刻不同

靜態庫的程式碼在編譯過程中已經被載入可執行程式,因此體積較大

共享庫的程式碼是在可執行程式執行時才載入記憶體的,在編譯過程中僅簡單的引用,因此程式碼體積較小。

3.靜態庫和動態庫的比較

接靜態庫其實從某種意義上來說只不過它操作的物件是目的碼而不是原始碼而已。因為靜態庫被連結後庫就直接嵌入可執行檔案中了,這樣就帶來了兩個問題。

(1)首先就是系統空間被浪費了。這是顯而易見的,想象一下,如果多個程式連結了同一個庫,則每一個生成的可執行檔案就都會有一個庫的副本,必然會浪費系統空間。

(2)再者,一旦發現了庫中有bug,挽救起來就比較麻煩了。必須一一把連結該庫的程式找出來,然後重新編譯。

而動態庫的出現正彌補了靜態庫的以上弊端。因為動態庫是在程式執行時被連結的,所以磁碟上只須保留一份副本,因此節約了磁碟空間。如果發現了bug或要升級也很簡單,只要用新的庫把原來的替換掉就行了。

但是靜態庫也有自己的優點:

編譯後的執行程式不需要外部的函式庫支援,因為所有使用的函式都已經被編譯進去了。

靜態庫的名字一般是libxxx.a(Linux)

動態庫的名字一般是libxxx.so (Linux),有時候也是 libxxx.so.major.minor,xxxx是該lib的名稱,major是主版本號, minor是副版本號

linux系統有幾個重要的目錄存放相應的函式庫,如/lib /usr/lib

4.如何判斷一個程式有沒有連結動態庫

(1)file命令

file程式是用來判斷檔案型別的,啥檔案一看都清楚明瞭。

(2)ldd命令

看動態庫,如果目標程式沒有連結動態庫,則列印“not a dynamic executable” (不是動態可執行檔案)

Linux靜態庫和動態庫學習總結 - 熊哥 - 熊哥

<file :run和run_dyn都是可執行檔案,action.h上一個ASCII C++檔案>

<ldd: run_dyn 使用了共享庫,action.o是不動態可執行檔案>

5.linux下庫檔案是如何產生的

(1)靜態庫

靜態庫的字尾是.a,它的產生分兩步

Step 1.由原始檔編譯生成一堆.o,每個.o裡都包含這個編譯單元的符號表

Step 2.ar命令將很多.o轉換成.a,成文靜態庫

(2)動態庫

動態庫的字尾是.so,它由gcc加特定引數編譯產生。

三、我的學習例子

1.要用到的工具

g++/gcc

ar

gcc常用引數

-L 載入庫檔案路徑

-l 指明庫檔名字

-fPIC 達到動態連結的目的,還有一個什麼要注意的,忘了

-I dir選項的功能,在標頭檔案的搜尋路徑列表中新增 dir 目錄

2.我的檔案

(1)doThing

//dosomething.h

  1: #ifndef DOSOMETHING_H 
  2: #define DOSOMETHING_H 
  3:  
  4: #include <string> 
  5:  
  6: void doThing(const std::string& strThing); 
  7:  
  8: #endif // DOSOMETHING_H 
  9: 

//dosomething.cpp

  1: #include "dosomething.h" 
  2: #include<iostream> 
  3:  
  4: void doThing(const std::string& strThing) 
  5: { 
  6:   std::cout << "start do thing :" << strThing << std::endl; 
  7: } 
  8: 

(2)Action類,使用doThing函式

//action.h

  1: #ifndef ACTION_H 
  2: #define ACTION_H 
  3: #include<string> 
  4:  
  5: using namespace std; 
  6:  
  7: class Action 
  8: { 
  9:   public: 
 10:     void setAction(const string& strAction); 
 11:     void doAction(void); 
 12:      
 13:   private: 
 14:     string mstrAction; 
 15: }; 
 16:  
 17: #endif // ACTION_H 
 18: 

//action.cpp

  1: #include "action.h" 
  2: #include "dosomething.h" 
  3:  
  4: void Action::setAction(const string& strAction) 
  5: { 
  6:   mstrAction = strAction; 
  7: } 
  8:  
  9: void Action::doAction(void ) 
 10: { 
 11:   doThing(mstrAction); 
 12: } 
 13: 

(3)主檔案

//main.cpp

  1: #include <iostream> 
  2: #include "action.h" 
  3:  
  4: int main(int argc, char **argv) 
  5: { 
  6:      
  7:   Action action; 
  8:   action.setAction("say hello!"); 
  9:   action.doAction(); 
 10:    
 11:   return 0; 
 12: } 
 13: 

 

linux下生成寫庫檔案程式碼好像更容易些,不用寫Windows下面那樣的在標頭檔案還要要寫DLL匯出,顯得很乾淨。

3.使用靜態庫的方法

基本命令:

g++ –c –o –L –l -I

ar cr 打包

Linux靜態庫和動態庫學習總結 - 熊哥 - 熊哥

最後生成了run,可以直接執行成功。

4.使用動態庫

基本命令:

g++ –c –o –shared –fPIC –L –l –I

生成so時加 –shared –fPIC

Linux靜態庫和動態庫學習總結 - 熊哥 - 熊哥

我在生成後立即執行有錯誤了。

原因:因為在動態函式庫使用時,會查詢/usr/lib、/lib目錄下的動態函式庫,而此時我們生成的庫不在裡邊。

這個時候有好幾種方法可以讓他成功執行:

(1)最直接最簡單的方法就是把so拉到/usr/lib或/lib中去,但這好像有點汙染環境吧?

(2)export LD_LIBRARY_PATH=$(pwd)

(3)可以在/etc/ld.so.conf檔案里加入我們生成的庫的目錄,然後/sbin/ldconfig

關於/etc/ld.so.conf

/etc/ld.so.conf裡面存放的是連結器和載入器搜尋共享庫時要檢查的目錄,預設是從/usr/lib /lib中讀取的,所以想要順利執行,我們也可以把我們庫的目錄加入到這個檔案中並執行/sbin/ldconfig 。

關於/etc/ld.so.cache

/etc/ld.so.cache裡面儲存了常用的動態函式庫,且會先把他們載入到記憶體中,因為記憶體的訪問速度遠遠大於硬碟的訪問速度,這樣可以提高軟體載入動態函式庫的速度了。

使用了第(2)種方法解決問題

Linux靜態庫和動態庫學習總結 - 熊哥 - 熊哥

四、其它涉及到的東西

(1)dlopen方法的動態庫顯式呼叫

(2)gcc的各個引數

(3)makefile的編寫

五、感謝

學習過程中參考了好多其它文章,由於之前存的TXT參考,無法查詢出處給出連結,在這裡謝謝作者,這裡僅供本人學習。

您可以對本文隨意轉載修改或使用,但請保持正確性,不要誤導他人。

本文出處:http://pppboy.blog.163.com/blog/static/302037962011112104720934/


相關文章