靜態庫與動態庫的簡單說明
一.靜態庫和動態庫的簡單介紹
程序設計的模塊化是人們一直在追求的目標,因為當一個系統十分復雜的時候,將系統模塊化既可以並行開發,又可以增強程序的可用性,降低程序間的耦合度。在一個復雜的多模塊系統中,
各個模塊編譯完成後,會生成各自的目標文件*.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
以上三種方法親測可用。
靜態庫與動態庫的簡單說明