【轉】靜態連接庫、動態連接庫和動態加載庫

轉自:https://www.cnblogs.com/nufangrensheng/p/3578784.htmlhtml

靜態連接庫linux

前言

靜態庫是obj文件的一個集合(目標文件中一般僅解析了文件內部的變量和函數,對於引用的函數和變量尚未解析,這須要將其餘已經編寫好的目標文件引用進來,將沒有解析的函數和變量進行解析,一般引用的目標是庫),一般靜態庫以".a"爲後綴,名字格式通常爲libxxx.a靜態庫由程序ar生成緩存

實例程序以下:函數

Main.c工具

#include <stdio.h>

extern void print_hello();

int main(void)
{
    print_hello();
}

Print_hello.cpost

#include <stdio.h>

void print_hello()
{
    printf("hello\n");
}
  1. 生成靜態連接庫

    建立靜態庫的步驟:測試

    1. 生成目標文件(使用命令gcc –c file.c)
    2. 使用工具ar對目標文件進行歸檔(使用的命令以下)

    生成靜態連接庫,或者將一個obj文件加入到已經存在的靜態庫的命令格式爲:spa

    ar –rcs 庫文件obj_1 obj_2 …3d

    使用上面的實例程序print_hello.c建立靜態連接庫:指針

  2. 使用靜態連接庫

    使用方式一:

    使用方式二:

    注意,在方法二中"-L./"不可少,不然出現以下錯誤:

    這是由於上面的命令在系統默認的路徑下查找hello函數庫,而咱們並無將libhello.a庫放在系統默認搜索路徑下,因此須要顯示指定庫函數的路徑爲當前目錄。

    另外還需注意,在使用-l選項時,-o選項的目標名稱要在-l連接的庫名稱以前,不然gcc會認爲-l是生成的目標而出錯。

    下面兩個圖分別是頭文件和庫文件的默認搜索路徑:

動態連接庫

前言

動態連接庫是程序運行時加載的庫,當動態連接庫正確安裝後,全部的 程序均可以使用動態庫來運行程序。動態連接庫是目標文件的集合,目標文件在動態連接庫中的組織方式是按照特殊方式造成的。庫中函數和變量的地址是相對地址,不是絕對地址,其真實地址在調用動態庫的程序加載時造成。

    動態連接庫的名稱有別名(soname)、真名(realname)和連接名(linker name):

    別名:libxxx.so,這種形式的庫名正是執行編譯命令時編譯器要搜索的名字。

    真名:動態連接庫的真實名稱,通常老是在別名的基礎上加上一個小版本號、發佈版本等構成。

    連接名:程序連接時使用的庫的名字。

  1. 生成動態連接庫

    生成動態連接庫的命令很簡單,使用-fPIC選項或者-fpic選項。-fPIC和-fpic選項的做用是使得gcc生成的代碼是位置無關的,例以下面的命令將print_hello.c編譯生成動態連接庫:

    其中-shared選項告訴編譯器生成一個動態連接庫;-soname,libhello.so表示生成動態庫的別名是libhello.so;-o libhello.so.1選項擇表示生成名字爲libhello.so.1的實際動態連接庫文件。

    生成動態連接庫以後一個很重要的問題就是安裝,通常狀況下將生成的動態連接庫複製到系統默認的動態連接庫的搜索路徑下,一般有/lib,/usr/lib,/usr/local/lib,放到其中任何一個目錄下均可以。

  2. 動態連接庫的配置

    動態連接庫並非能夠隨意地使用,要在運行的 程序中使用動態連接庫,須要指定系統的動態連接庫搜索的路徑讓系統找到運行所須要的動態連接庫才能夠。

    系統中的配置文件/etc/ld.so.conf是動態連接庫的搜索路徑配置文件。這這個文件內,存放着可被Linux共享的動態連接庫所在目錄的名字系統目錄/lib,/usr/lib除外,這兩個目錄默認就是動態連接庫的搜索路徑,多個目錄名間以空白字符(空格、換行等)或冒號或逗號分割。查看系統中的動態連接庫配置文件的內容:

    Linux的配置文件將目錄ld.so.conf.d/中的配置文件包含了進來。

  3. 動態連接庫管理命令

    爲了讓新增長的動態連接庫可以被系統共享,須要運行動態連接庫的 管理命令ldconfigldconfig命令的做用是在系統的默認搜索路徑和動態連接庫配置文件中所列出的目錄裏搜索動態連接庫,建立動態連接庫裝入程序須要的連接和緩存文件。搜索完畢後,將結果寫入緩存文件/etc/ld.so.cache中,文件中保存的是已經排好序的動態連接庫名字列表。

    ldconfig命令的用法以下:

  4. 使用動態連接庫

    把當前工做目錄(libhello.so.1所在的目錄)加入動態連接庫的搜索路徑配置文件/etc/ld.so.conf中

    執行ldconfig命令刷新緩存文件/etc/ld.so.cache:

    咱們會發現,執行ldconfig命令後,當前目錄下生成了一個新的文件libhello.so(這是-soname,libhello.so選項致使的,若是沒有該選項,那麼ldconfig執行後不會生成新文件libhello.so。),使用ls命令會發現此文件是libhello.so.1的連接文件:

    在編譯程序時,使用動態連接庫和靜態連接庫是一致的,使用"-lxxx"的方式:

    編譯後執行時出現以下錯誤:

    其主要緣由是使用了SELINUX。將其狀態設置爲disabled便可,方法以下:
    打開/etc/selinux/config,將selinux=enforcing或permissive改爲disabled。而後存盤退出,重啓系統。

    再次執行,結果以下:

    運行時,還可能出現這樣的錯誤:test: error while loading shared libraries: libxxx.so: cannot open shared object file: No such file or directory,這是因爲程序運行時沒有找到動態連接庫形成的。程序編譯時連接動態連接庫和運行時使用動態連接庫的概念是不一樣的,在運行時,程序連接的動態連接庫須要在系統目錄下才行。有幾種辦法能夠解決此種問題:
    1)將動態連接庫的目錄放到程序搜索路徑中,能夠將庫的路徑添加到環境變量LD_LIBRARY_PATH中實現:

    2)使用ld-Linux.so.2來加載程序,命令格式爲:
    /lib/ ld-Linux.so.2 –library-path 路徑 程序名
    加載test程序的命令爲:

    不過貌似並非全部系統上都有ld-Linux.so.2。

    注意,若是系統的搜索路徑下同時存在靜態連接庫和動態連接庫,默認狀況下會連接動態連接庫。若是須要強制連接靜態連接庫,須要加上"-satic"選項。

動態加載庫

前言

    動態加載庫和通常的動態連接庫所不一樣的是,通常動態連接庫在程序啓動時就要尋找動態庫,找到庫函數;而動態加載庫能夠用程序的方法來控制何時加載。動態加載庫主要有函數dlopen()、dlerror()、dlsym()和dlclose()來控制動態庫的使用。函數原型以下:

  1. 打開動態庫dlopen()

    函數dlopen()按照用戶指定的方式打開動態連接庫其中參數filename爲動態連接庫的文件名,flag爲打開方式,通常爲RTLD_LASY,函數的返回值爲庫的指針。

    例如,下面的代碼使用dlopen打開當前目錄下的動態庫libhello.so:

    void *phandle = dlopen("./libhello.so", RTLD_LAZY);

  2. 得到函數指針dlsym()

    使用動態連接庫的目的是調用其中的函數,完成特定的功能。函數dlsym()能夠得到動態連接庫中指定函數的指針而後可使用這個函數指針進行操做。其中參數handle爲dlopen()打開動態庫後返回的句柄,參數symbol爲函數的名稱,返回值爲函數指針。

  3. 使用動態加載庫實例

    #include <stdio.h>
    #include <dlfcn.h>
    
    int main(void)
    {
        void (*printhello)(void);
        void *phandle = NULL;
        char *perr = NULL;
    phandle
    = dlopen("./libhello.so", RTLD_LAZY); if(!phandle) { printf("Failed load library!\n"); } perr = dlerror(); if(perr != NULL) { printf("%s\n", perr); return(0); } printhello = dlsym(phandle, "print_hello"); perr = dlerror(); if(perr != NULL) { printf("%s\n", perr); return(0); } (*printhello)(); dlclose(phandle);
    return(0); }

    編譯運行:

    注意,想要使用dlfcn.h中的函數,編譯時必須加上選項-ldl

小發現:

  有沒有注意到,咱們並無使用用到include <hello.h>(假設咱們爲print_hello.c寫了一個hello.h的頭文件)。其實,只要編譯命令中加入選項-lhello,hello.h頭文件包含不包含都沒有問題。爲了驗證這個問題,使用http://www.cnblogs.com/nufangrensheng/p/3518411.html中的程序清單11-1這個實例進行了測試:

  在http://www.cnblogs.com/nufangrensheng/p/3518411.html程序清單11-1編譯過程當中,曾遇到undefined reference to ‘pthread_create’這樣的錯誤,緣由在於沒有在編譯命令中加入-lpthread選項。

  若是咱們加入了-lpthread選項,此時嘗試着把程序中的#include <pthread.h>去掉,從新編譯你會發現一樣也是能夠的。

  總結:若是使用的是標準庫中的函數,則只需將頭文件包含進來。若是使用的函數所在的庫不是標準庫(例如咱們本身編寫的庫或標準之外擴展的庫),則在編譯時必須加入-lxxx選項,而頭文件則能夠包含也能夠不包含,包含進來顯得規範而已。

相關文章
相關標籤/搜索