1. 程式人生 > >Linux動態庫.a與動態庫.so的生成與區別、以及.so庫檔案的封裝與使用

Linux動態庫.a與動態庫.so的生成與區別、以及.so庫檔案的封裝與使用

一、前言

如果有公司需要使用你們產品的一部分功能(通過程式碼呼叫這些功能),如果不想提供原始碼,那麼就可以通過封裝成庫檔案的形式提供給對方使用。本文主要介紹了生成動態庫與靜態庫檔案的過程、以及封裝和使用庫檔案的方法。

二、靜態庫.a與動態庫.so的生成與區別

.o檔案 :二進位制目標檔案,可用於打包成庫檔案也可以連結生成可執行檔案;
c檔案編譯後連結,生成可執行檔案

gcc  test1.c test2.c test3.c test_main.c -o test_main
./test_main

這裡寫圖片描述
將.o目標檔案連結生成可執行檔案

gcc -c  test1.c test2.c
test3.c test_main.c//編譯成.o目標檔案 gcc test1.o test2.o test3.o test_main.o -o test_main_1//把.o檔案連結成可執行檔案 ./test_main_1

這裡寫圖片描述
原始碼test1.h

#include <stdio.h>
    void log_1();//函式在.h標頭檔案中宣告

原始碼test1.c

#include "test1.h"
//函式在.C檔案中實現
void log_1(){
        printf("This is file test1!\n");
}

原始碼test2.h

#include <stdio.h>
void log_2();

原始碼test2.c

#include "test2.h"

void log_2(){
        printf("This is file test2!\n");
}

原始碼test3.h

#include <stdio.h>
    void log_3();

原始碼test3.c

#include "test3.h"

void log_3(){
        printf("This is file test3!\n");
}

原始碼test_main.h

#include <stdio.h>
#include "test1.h"//引入標頭檔案 #include "test2.h" #include "test3.h" void log_1();//宣告test1.h中的函式 void log_2(); void log_3();

原始碼test_main.c

#include "test_main.h"
int main(int argc, char* argv[]){

        log_1();//呼叫test1.h中的函式
        log_2();
        log_3();
        return 0;
}

.a檔案 :靜態庫檔案,靜態庫在編譯時已經被連結到目的碼中,執行程式不依賴該靜態庫檔案;
優點:將程式使用的函式的機器碼複製到最終的可執行檔案中,提高了執行速度;如果庫函式改變,整個程式需要重新編譯
缺點:所有需用到靜態庫的程式都會被新增靜態庫的那部分內容,使得可執行程式碼量相對變多,佔用記憶體和硬碟空間

ar rc libtest.a test1.o test2.o test3.o//把.o檔案打包成.a的庫檔案
gcc test_main.c -L. -ltest -o test_main_a//連結生成可執行檔案
./test_main_a//執行測試程式
rm libtest.a //刪除靜態庫檔案
./test_main_a//同樣正常執行程式

這裡寫圖片描述
.so檔案:動態庫檔案,在程式執行的時候載入動態庫檔案,程式要正常執行必須依賴於它需要使用的動態庫檔案;
優點:只有在程式執行的時候, 那些需要使用的函式程式碼才被拷貝到記憶體中。動態庫在記憶體中可以被被多個程式使用,又稱之為共享庫,節約了記憶體和磁碟空間,以時間效率去換取空間效率;當呼叫介面沒改變,庫檔案發生改變重新編譯,而呼叫的主程式可不用重新編譯;
缺點:執行速度不及靜態庫檔案;
靜態庫與動態庫的選取使用,請結合自己的場景進行取捨,我不知道客戶要使用的頻率,我選擇使用動態庫的形式;

三、.so庫檔案的封裝以及使用

首先檢視目錄層次,可以看到就當前目錄分別由一個lib的庫目錄、Makefile、ReadMe.txt、測試呼叫庫函式的原始碼和使用者使用的庫標頭檔案

ls -R

這裡寫圖片描述

也可以安裝tree工具,檢視所有檔案更有目錄層次

sudo apt-get install tree//安裝
sudo tree --help//檢視命令選項
tree -a//列出當前目錄的所有檔名(檔案和資料夾)

這裡寫圖片描述
最重要就是把lib裡面的所有檔案編譯成一個.so的庫檔案;
lib庫裡的Makefile

OBJS:=adTorgb.cpp.o  descriptors.c.o  error.c.o  linux.c.o  satusbimage.cpp.o  usb.c.o

#把所有[.c]檔案編譯成[.o]檔案
#-fPIC; 代表編譯為位置獨立的程式碼,滿足了不同的程序對所載入動態庫的共享;
#-c; 表示只編譯原始檔但不連結;
#$<; 表示所搜尋到與第一個相匹配的檔案,即第一個[.c]檔案;
#-o; 指定輸出檔名;
#[email protected]; 與[.c]檔案相對應的[.o]檔案;
#-I.; 需用到的標頭檔案在本目錄中找.
%.c.o:%.c 
        gcc -fPIC -c $< -o [email protected] -I.

%.cpp.o:%.cpp
        g++ -fPIC -c $< -o [email protected] -I.

#-shared: 該選項指定生成動態連線庫
all:$(OBJS)
        @echo "Compile..."
        g++ -shared -fpic -o libsatusb.so $(OBJS)
        @echo "End"

clean:
        rm -fr *.o *.so

對比封裝lib庫與測試目錄satusbimage.h的區別,以下為封裝庫的標頭檔案:

#ifndef _SATUSBIAMGE_H_
#define _SATUSBIMAGE_H_
#endif
#include <usb.h>
#include "color_tables_rgb.h"


#define USB_VID         0x0547          //CY7C68014A的產商ID
#define USB_PID         0x0503          //CY7C68014A的產品ID

#define EP0ADDR         0x01            //埠0地址,通道0
#define EP1ADDR         0x81            //埠1地址,通道1
#define EP2ADDR         0x02            //埠2地址,通道2
#define EP3ADDR         0x86            //埠3地址,通道3
#define USB_TIMEOUT     10000           //傳輸資料的時間延遲

#define IR_ROW 288
#define COL 1024
#define IR_IMAGE_SIZE       IR_ROW*COL*2        //IR一幀影象的大小

/*
 *@find_device. We can find out the USB device that we need to use from USB bus. We need to open USB device for getting USB handle.  
 *@param   Void 
 *@return  Non-null value indicates that the function is called successfully, and the function of return value is an USB device. 
 */
struct usb_device* find_device();

/*
 *@open_device   We can obtain a handle from the USB device after function call,the handle can be used to the parameter of reading Usb data.
 *@param  <dev>  Dev  means that we will choose which USB device to open.
 *@return Non-null value indicates that the function is called successfully. and the function of return value is a USB handle.  
*/
usb_dev_handle* open_device(struct usb_device* dev);

/*
 *@bulk_read_data. If you want to watch the image, please use the bulk_read_data function.
 *@parameter <handle> We read data from the USB handle.<data_ad> Data_ad is used to save collected image of ad value.<data_rgb> Data_rgb is used to save the having been disposed of RGB of image data.
*@return Value greater than zero indicates that the function is called successfully.
*/
int bulk_read_data(usb_dev_handle* handle, char* data_ad, _rgb_item* data_rgb);

/*@close_usb_handle. When you do not keep watch on the image, please use the close_usb_handle function to close the usb device,the parameter device_handle is the open_device function's return value. 
 *@parameter <handle> We will close the USB device through hanlde. the parameter device_handle is the open_device function's return value. 
 *@return Value greater than zero indicates that the function is called successfully. 
*/
int close_usb_handle(usb_dev_handle* handle);

/*@set_level_value. To set the brightness of picture 
 *@parameter <value> The value of brightness
 *@return Return true if the parameter value between 0 and 255,else return false
*/
bool set_level_value(int value);

/*@set_span_value. To set the contrast of picture 
 *@parameter <value> The value of contrast
 *@return Return true if the parameter value between 0 and 255,else return false
*/
bool set_span_value(int value);

以下是測試函式所用到的標頭檔案;

#ifndef _SATUSBIMAGE_H_
#define _SATUSBIMAGE_H_
#endif
#include <usb.h>

#define IR_ROW 288
#define COL 1024
#define IR_IMAGE_SIZE       IR_ROW*COL*2       
struct _rgb_item
{
        unsigned char r;
        unsigned char g;
        unsigned char b;
        unsigned char reserved;
};
typedef struct _rgb_item rgb_item;

/*
 *@find_device. We can find out the USB device that we need to use from USB bus. We need to open USB device for getting USB handle.  
 *@param   Void 
 *@return  Non-null value indicates that the function is called successfully, and the function of return value is an USB device. 
 */
struct usb_device* find_device();

/*
 *@open_device   We can obtain a handle from the USB device after function call,the handle can be used to the parameter of reading Usb data.
 *@param  <dev>  Dev  means that we will choose which USB device to open.
 *@return Non-null value indicates that the function is called successfully. and the function of return value is a USB handle.  
*/
usb_dev_handle* open_device(struct usb_device* dev);

/*
 *@bulk_read_data. If you want to watch the image, please use the bulk_read_data function.
 *@parameter <handle> We read data from the USB handle.<data_ad> Data_ad is used to save collected image of ad value.<data_rgb> Data_rgb is used to save the having been disposed of RGB of image data.
*@return Value greater than zero indicates that the function is called successfully.
*/
int bulk_read_data(usb_dev_handle* handle, char* data_ad, _rgb_item* data_rgb,unsigned char color_table);

/*@close_usb_handle. When you do not keep watch on the image, please use the close_usb_handle function to close the usb device,the parameter device_handle is the open_device function's return value. 
 *@parameter <handle> We will close the USB device through hanlde. the parameter device_handle is the open_device function's return value. 
 *@return Value greater than zero indicates that the function is called successfully. 
*/
int close_usb_handle(usb_dev_handle* handle);

/*@set_level_value. To set the brightness of picture 
 *@parameter <value> The value of brightness
 *@return Return true if the parameter value between 0 and 255,else return false
*/
bool set_level_value(int value);

/*@set_span_value. To set the contrast of picture 
 *@parameter <value> The value of contrast
 *@return Return true if the parameter value between 0 and 255,else return false
*/
bool set_span_value(int value);

通過對比發現,兩個標頭檔案並不一樣,如果不想讓一些資訊透露給三方,就可以封裝成庫,給第三方使用者提供需要呼叫的介面即可,具體的實現在lib庫中的.cpp中;
測試函式資料夾中的Makefile


all:
        #-L./lib: 編譯時到當前路徑的lib資料夾中去需找libsatusb.so庫檔案
        gcc satimagetest.c -L./lib -lsatusb -o satimagetest
        #如果要執行編譯完成的可執行檔案,必須得設定下環境變數(執行程式時會去連結這個.so庫檔案,如果不設定環境變數,就找不到該庫檔案,程式執行失敗),或者把生成的.so庫檔案拷貝到已設定的路徑下(可以查環境變數LD_LIBRARY_PATH,但移植性不高)。
#export LD_LIBRARY_PATH=/opt/satImage/lib/:$LD_LIBRARY_PATH

庫檔案的封裝就介紹到此。