1. 庫是什麼
庫(library),在計算機軟件領域應該十分常見,從專業術語的角度來看,庫是具有某種特定功能的封裝好的模塊。通俗來說,庫是一堆函數的集合。那麼爲什麼需要庫呢?其實這是非常容易理解的,庫的作用和函數的功能都是相似的,都是爲了封裝好功能模塊,以便於代碼重用,避免重複造輪子,而庫和單純的函數是有區別的,庫屬於文件,函數屬於代碼!
舉一個例子,在hello.c文件中,使用 abs() 函數計算絕對值,我們應該通過 #include <math.h> 包含頭文件,與庫中的接口關聯起來,這樣子,在執行程序過程中程序就會去 libc.so 調動相關函數。得出如下關係圖:
- libc.so是C語言標準庫,提供很多常見功能,例如math、標準輸入輸出等
2. 動態庫和靜態庫
庫分爲兩種類型:動態庫(也稱爲共享庫)和靜態庫,動態庫和靜態庫總的來說最大的區別就是,庫在編譯階段會不會被編譯到可執行程序中。
很顯然,一開始舉的例子屬於動態庫,因爲這個庫不存在於可執行程序中,調用接口需要訪問外面的文件。
那麼動態庫以及靜態庫有什麼特點呢?
動態庫:
- 優點
- 由於沒有把動態庫編譯進可執行文件中,可執行文件大小較小;
- 動態庫調入內存後,可供多個文件調用,減少內存消耗;
- 需要修改庫時,無需將可執行文件重新編譯,只需重新編譯庫,使用更加靈活。
- 缺點
- 可執行程序運行時調用接口,需要訪問動態庫文件,執行速度下降;
- 可執行程序運行依賴於動態庫環境,動態庫環境破壞,可能使可執行程序運行異常。
靜態庫:
- 優點
- 庫被編譯進可執行程序中,執行效率更高;
- 可執行程序不受庫環境影響,即使庫文件刪除或者移動了依舊可以正常執行。
- 缺點
- 庫被編譯進可執行程序中,可執行程序大小較大;
- 修改庫需要重新編譯庫以及源程序,使用不靈活。
3. 庫的製作及使用
我們通過庫的製作和使用更加深入的瞭解庫的作用。
我們製作提供 add() 功能的add庫,並編寫 test 程序去測試庫功能。
文件名稱 | 文件介紹 |
test.c | .c源文件 |
add.h | add庫對應的.h頭文件 |
add.c | add庫.c源文件 |
-
test.c
#include <stdio.h>
#include "add.h"
int main(){
int a = 1, b = 2, sum = 0;
sum = add(a, b);
printf("sum=%d\n", sum);
return 0;
}
-
add.h
#ifndef _ADD_H_
#define _ADD_H_
/*
*@brief: add fun
*@param1: value a
*@param2: value b
*@retval: sum
*/
int add(int a, int b);
#endif
-
add.c
#include "add.h"
/*
*@brief: add fun
*@param1: value a
*@param2: value b
*@retval: sum
*/
int add(int a, int b){
return (a+b);
}
製作如上幾個文件。下面開始分別製作使用動態庫、靜態庫:
3.1 動態庫的製作、使用
在我的家目錄下有這麼一個目錄( /home/oracle/lib/test1/ )有如下文件:
製作動態庫:
使用命令:
gcc -shared -fpic -o libadd.so add.c # -shared: 製作動態庫(共享庫) # -fpic: 聲明生成代碼與位置無關 # -o: output 後接輸出文件名 # libadd.so: 需要命名爲 libXXX.so 也可以帶版本號例如 libXXX.so.1
對於 -fpic參數需要詳細瞭解的可以閱讀如下文章:
linux鏈接動態庫 fpic的使用
編譯測試程序源文件
gcc -o test test.c -L ./ -ladd # -o: output 後接輸出文件名 # test: 輸出文件名 # -L: 後接所需庫的目錄路徑 # ./: 所需庫在當前目錄,所以是 "./" # -ladd: 所需的庫, 需要寫成 -lXXX, XXX爲庫名
運行測試程序
會發現,直接運行測試程序會出錯,提示無法打開共享對象文件,沒有找到此文件。這是,我們就要注意了,編譯程序需要制定庫所在的目錄以及庫名,那麼運行的時候也許要指定,不過不同的是,不能像編譯階段那樣子手動指定,而是通過配置系統環境變量的方式。主要有三種方式:
1. 通過配置 LD_LIBRARY_PATH 環境變量
LD_LIBRARY_PATH是Linux環境變量名,該環境變量主要用於指定查找共享庫(動態鏈接庫)時除了默認路徑外的其他路徑。(上述默認路徑指的是:/lib、/usr/lib 等)
- 臨時配置:
通過如下命令導入路徑:
export LD_LIBRARY_PATH=/home/oracle/lib/test1:$LD_LIBRARY_PATH # export: 設置環境變量工具 # /home/oracle/lib/test1: 庫對應的目錄 # $LD_LIBRARY_PATH: 取出原來庫的值,相當於追加,不改變原來的值
通過如下命令確認是否導入成功:
echo $LD_LIBRARY_PATH # echo: 環境變量查看工具 # $LD_LIBRARY_PATH: 環境變量的值
臨時配置操作簡單,但是臨時配置關機後LD_LIBRARY_PATH會重置,也就是說,臨時配置只對這次開機有效,關機重啓需要再次設置。
- 永久配置:
永久配置其實原理上和臨時配置是一樣的,只不過是配置腳本添加進系統啓動中,使其每次啓動自動配置環境變量,常見的啓動腳本有 :
/etc/profile #需要root權限,對整個系統有效 /home/xxx(根據用戶名變化)/.bashrc #對特定用戶有效
在家目錄下的 .bashrc 最後一行加入:
export LD_LIBRARY_PATH=/home/oracle/lib/test1:$LD_LIBRARY_PATH
source /home/oracle/.bashrc #使環境變量立即生效
那麼,就完成通過修改 LD_LIBRARY_PATH 環境變量永久配置了
2. 通過修改 /etc/ld.so.conf 文件
通過文件名,我們就可以知道這個文件是動態鏈接庫的配置文件,以下是操作一覽圖:
ldconfig這個程序,位於/sbin下,它的作用是將文件/etc/ld.so.conf列出的路徑下的庫文件緩存到/etc/ld.so.cache以供使用,因此當安裝完一些庫文件,或者修改/etc/ld.so.conf增加了庫的新的搜索路徑,需要運行一下ldconfig,使所有的庫文件都被緩存到文件/etc/ld.so.cache中,如果沒做,可能會找不到剛安裝的庫。
3. 將庫文件移動到動態鏈接庫存放默認目錄
默認路徑指的是:/lib、/usr/lib,將庫文件拷貝到此目錄這無需配置環境,操作最簡單,但是可能造成系統庫默認目錄混亂。
3.2. 靜態庫的製作、使用
在我的家目錄下有這麼一個目錄( /home/oracle/lib/test1/ )有如下文件:
製作靜態庫
編譯測試程序源文件
運行測試程序
由於庫直接編譯進測試程序,可直接運行。