1. 程式人生 > >靜態庫與動態庫的簡單說明

靜態庫與動態庫的簡單說明

外部程序 endif ldconfig 開始 director 有時 鏈接 現在 con

  一.靜態庫和動態庫的簡單介紹

  程序設計的模塊化是人們一直在追求的目標,因為當一個系統十分復雜的時候,將系統模塊化既可以並行開發,又可以增強程序的可用性,降低程序間的耦合度。在一個復雜的多模塊系統中,

各個模塊編譯完成後,會生成各自的目標文件*.o,最後通過鏈接器將各個模塊鏈接起來生成可執行文件。

庫其實就是一個模塊文件。人們為了將一些功能模塊提供給他人使用,同時又不想將源代碼直接分發給別人(也可能是不需要,畢竟庫使用更方便,不用重新編譯),就將功能模塊做成庫,

外部應用通過鏈接庫來加載庫的功能模塊。比如glibc是GNU標準的C標準函數庫。

庫有靜態和動態之分。靜態庫在編譯時被鏈接進可執行文件中,動態庫是在程序運行時鏈接。靜態庫的優點是使用方便,只要編譯時鏈接成功,在程序運行時就不會有找不到庫或者庫錯亂的

問題,但是這也造成了升級更新困難和內存空間浪費的問題。對於靜態庫來說,升級就必須重新編譯應用程序鏈接靜態庫然後全部升級,而且如果多個應用程序都用到了同一個靜態庫,那麽當多個

應用程序運行時,內存中就會有靜態庫的多個拷貝,十分浪費空間。因為這些原因,動態庫應運而生。動態庫是程序運行時才鏈接,所以在程序更新時,我們只需要更新動態庫文件,重新啟動應用

程序就會鏈接新庫,而且當內存中已經有一個動態庫的拷貝時,其他應用在運行時,如果需要鏈接庫,會先從內存中查找庫,找到後就直接鏈接該庫,找不到再加載庫到內存中,這保證了不會有同

一個庫的多個拷貝在內存中占用空間。

  二.靜態庫和動態庫的編譯和鏈接 

  2.1 程序源碼

  我們將會創建一個app,主要功能是輸出“hello world”,具體實現在庫libhello.a/libhello.so中實現。下面是各個文件的源代碼:

      

  app源碼

  #include<stdlib.h>

  #include<stdio.h>

  #include"hello.h"

  void main(void)

  {

  hello();

   hello();

  }

  

  庫源碼

  

  hello.c源碼

  #include<stdlib.h>

  #include<stdio.h>

  void hello(void)

  {

  printf("\n======hello world======\n");

  }

  hello.h源碼

  #ifndef HELLO_H

  #define HELLO_H

  void hello(void);

  #endif

  2.2.靜態庫的編譯和鏈接

  

  第一步:生成目標文件

    # gcc -c hello.c

    生成目標文件hello.o

  第二步:編譯生成靜態鏈接庫libhello.a

    # ar rcs libhello.a hello.o

  第三步:增加用於外部調用靜態庫函數的頭文件hello.h

    hello.h頭文件的作用是給外部調用庫函數提供函數聲明

  第四步:外部程序使用靜態庫

    在app.c中包含所用靜態庫函數聲明的頭文件hello.h

    生成app.o的目標文件

    #gcc -c app.c

    鏈接靜態庫libhello.a生成可執行文件app

    #gcc -o app app.o -L. -lhello

    執行app

    #./app

      ======hello world======

      ======hello world======

  2.3.動態庫的生成

  同樣是先生成目標文件hello.o,然後編譯動態庫文件libhello.so

  # gcc -shared -fPCI -o libhello.so hello.o

  # ls

  發現libhello.so已經生成了,然後我們開始生成可執行文件。這裏動態庫並沒有鏈接。

  # gcc -o app app.o -L. -lhello

  編譯通過,執行app時出錯

  #./app

  ./app: error while loading shared libraries: libhello.so: cannot open shared object file: No such file or directory

  程序運行時,找不到動態庫。那為什麽編譯時能通過呢?因為編譯時的-L指定了編譯路徑,所以編譯能通過,但是在運行加載動態庫時,會默認從/lib,/usr/lib路徑下去找,而實際庫不在該路徑下,所以加載失敗。

  

  解決的辦法有三個:

  1.將庫文件拷貝到默認的動態庫路徑/lib,/usr/lib,此為最簡單,最快捷的方法

  2.程序編譯時指定動態庫路徑,使用“-Wl,-rpath”。

    # gcc -o app app.o -L. -lhello -Wl,-rpath=/mnt/hgfs/share/workspace/demo/LibraryDemo1

  註意:有的系統中可能是"-Wl,-rpath,/path/to/dir",使用時需要註意。還有就是我自己在使用時出現的小問題,提醒下大家,我開始總是寫成-Wl,rpath=path,結果編譯怎麽都不過,總是提示找不打rpath=path這個文件或路徑,後來才發現少寫了個“-”,提醒大家需要認真看錯誤提示。

  3.就是更改/etc/ld.so.conf。查看/etc/ld.so.conf發現它包含了/etc/ld.so.conf.d目錄下的所有*.conf。到/etc/ld.so.conf.d/目錄下,

   新建一個*.conf,在裏面加上動態庫的路徑(你也可以直接在其中某一個conf中增加一條,但是最好不要這麽幹,否則以後可能有未知的混亂)。

   此時運行app仍然失敗,原因是系統查找動態庫是通過查找/etc/ld.so.cache緩存,僅僅更改配置是不行的,還需要更新緩存。此時可以通過/sbin/ldconfig更新緩存。

   # /sbin/ldconfig

   有時候想知道動態庫路徑是否加入到緩存中,可以使用ldconfig指令

   # sudo /sbin/ldconfig -p |grep path

   在執行更新緩存後,可以查看到:

   # libhello.so (libc6) => /mnt/hgfs/share/workspace/demo/LibraryDemo1/libhello.so

    以上三種方法親測可用。

靜態庫與動態庫的簡單說明