<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 134,comments - 22,trackbacks - 0

    1.庫文件簡介

    庫文件是一個包含了編譯后代碼、數據的文件,用于與程序其他代碼連編,它可以使得程序模塊化、編譯速度更快,并且易于更新。庫文件分為三種(實質為兩種,在隨后兩句話有解釋):靜態庫(在程序之前就已經裝載進其中了)、共享庫(在程序啟動之時加載進去,在程序直接共享)、動態加載庫(dynamically loaded,DL)(在程序運行中任何時候都可以被加載進程序中使用,事實上DL并非是一個完全不同的庫類型,共享庫可以用作DL而被動態加載(靜態庫在Linux貌似無法用dlopen加載)。注意有些人使用dynamically linked libraries (DLLs)來指代共享庫,有些人使用DLL這個詞來形容任何可以被用作DL的庫文件,這個請區分對待。

    在具體使用中,我們應該多使用共享庫,這使得用戶可以獨立于使用該庫文件的程序而更新庫。DL的確非常有用,但有時候我們可能并不需要那些靈活性,而對于靜態庫,由于更新起來實在費勁,我們一般不使用。

    2.靜態庫的建立

    靜態庫就是一堆普通的目標文件(object file),習慣上靜態庫以.a為后綴,這是使用ar命令生成的。靜態庫允許用戶不用重新編譯代碼就可以鏈接程序,以節省重新編譯的時間,其實這個時間已經在強大的機器配置和快速的編譯器中顯得微不足道了,這個常常用來提供程序而不是源代碼。速度上,靜態ELF(Executable and Linking Format)庫文件比共享庫或者動態加載庫快1%-5%,但實際上常常因為其他因素而并不一定快。

    我們寫主文件prog.c:

      1: #include
      2: void ctest1(int *);
      3: void ctest2(int *);
      4:
      5: int main()
      6: {
      7:    int x;
      8:    ctest1(&x);
      9:    printf("Valx=%d\n",x);
     10:
     11:    return 0;
     12: }
     13: 然后寫這兩個函數的實現:

    ctest1.c

      1: void ctest1(int *i)
      2: {
      3:    *i=5;
      4: }       ctest2.c

      1: void ctest2(int *i)
      2: {
      3:    *i=100;
      4: }我們首先編譯這兩個函數實現的源文件:

    gnuhpc@gnuhpc-desktop:~/MyCode/lib/statics$ gcc -Wall -c ctest1.c ctest2.c
    gnuhpc@gnuhpc-desktop:~/MyCode/lib/statics$ ls
    ctest1.c  ctest1.o  ctest2.c  ctest2.o  prog.c

    然后創建靜態庫libctest.a:

    gnuhpc@gnuhpc-desktop:~/MyCode/lib/statics$ ar -cvq libctest.a ctest1.o ctest2.o

    a - ctest1.o
    a - ctest2.o

    我們查看一下這個庫中的文件:

    gnuhpc@gnuhpc-desktop:~/MyCode/lib/statics$ ar -t libctest.a
    ctest1.o
    ctest2.o

    此時我們可以編譯我們的程序了,注意-l選項,后邊的參數是去掉lib和.a的部分,并且需要放在要編譯的文件名之后,否則會報錯。:

    gnuhpc@gnuhpc-desktop:~/MyCode/lib/statics$ gcc -o test prog.c -L./ –lctest
    gnuhpc@gnuhpc-desktop:~/MyCode/lib/statics$ ls
    ctest1.c  ctest1.o  ctest2.c  ctest2.o  libctest.a  prog.c  test
    gnuhpc@gnuhpc-desktop:~/MyCode/lib/statics$ ./test
    Valx=5

    3.共享庫的建立

    共享庫是在程序啟動時加載的庫文件。當共享庫加載完畢后所有啟動的起來的程序都將使用新的共享庫。在創建共享庫之前,還需要了解一些知識:

    命名規則:
    每一個共享庫都有一個soname,一般都形如libname.so.versionNumber,其中versionNumber每當接口發生改變時都要增加,一個完全的soname的前綴應該是它所在目錄,在一個實際系統中,一個完整的soname只是共享庫文件的real name的符號鏈接。程序運行時在內部列出所需的共享庫時使用的就是soname。
    每一個共享庫也有一個real name,這是包含實際代碼的文件名,real name使用soname為前綴,并且在后邊添加一些信息,一般都形如soname.MinorNumber.ReleaseNumber。 最后的releaseNumber可有可無。這個是生成共享庫時實際文件的名稱。
    同時,在編譯器要求使用一個共享庫時使用的名字稱為linker name,一般都是去掉版本號的soname,用于gcc中-lname這樣的選項的編譯。
    這幾個名字的關系:你在創建實際庫文件中指定libreadline.so.3.0為real name ,并且使用符號鏈接創建soname ->libreadline.so.3和linker name-> /usr/lib/libreadline.so。
    放置位置:
    GNU標準推薦將所有默認的庫安裝在/usr/local/lib,這指的是開發者源代碼默認的位置。
    FHS指出大多數的庫文件應該放在/usr/lib,而啟動所需的庫則應該放在/lib中,而非系統庫應該放在/usr/local/lib。這指的是發行版默認的位置,這兩個標準并沒有矛盾。
    共享庫的主要有三個步驟:

    創建目標代碼。
    創建庫。
    使用符號鏈接創建默認版本的共享庫(可選)。
    現在我們舉個例子來說明,首先我們編譯源代碼,使用-fPIC選項生成共享庫所需的位置獨立代碼(position-independent code (PIC)):

    gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ gcc -Wall -fPIC -c *.c
    gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ ls
    ctest1.c  ctest1.o  ctest2.c  ctest2.o  prog.c  prog.o

    然后我們創建庫文件:

    gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ gcc -shared -Wl,-soname,libctest.so.1 -o libctest.so.1.0   *.o
    gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ ls
    ctest1.c  ctest1.o  ctest2.c  ctest2.o  libctest.so.1.0  prog.c  prog.o

    -shared選項指明生成共享目標文件,-W1(注意是小寫L而不是一)指明傳入鏈接器的參數,在此我們設定了該庫的soname為libctest.so.1,-o則指明了生成的目標庫文件為libctest.so.1.0(這個就是real name)。

    最后創建所需的符號鏈接:

    gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ sudo mv libctest.so.1.0 /usr/local/lib/libctest.so.1.0
    gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ sudo ln -sf /usr/local/lib/libctest.so.1.0 /usr/local/lib/libctest.so.1
    gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ sudo ln -sf /usr/local/lib/libctest.so.1.0 /usr/local/lib/libctest.so

    創建的libctest.so就是上面所謂linker name,用于編譯時-lctest選項。

    創建的libctest.so.1就是soname,我們在上邊說過程序在運行時需要這個名字的符號鏈接。

    此時我們的共享庫就建好了,接著我們編譯程序:

    gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ gcc -Wall -L/usr/local/lib prog.c -lctest -o prog
    gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ ls
    ctest1.c  ctest1.o  ctest2.c  ctest2.o  prog  prog.c  prog.o

    我們編譯完畢,該庫并不會包含在可執行文件中,只有在執行時來會動態加載進來。我們可以通過ldd列出一個可執行程序所有的依賴,在我的系統中還找不到/usr/local/bin的路徑:

    gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ ldd prog
        linux-gate.so.1 =>  (0x00a5c000)
        libctest.so.1 => not found
        libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0x00a6f000)
        /lib/ld-linux.so.2 (0x00451000)

    此時,運行會報找不到庫的錯誤:

    gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ ./prog
    ./prog: error while loading shared libraries: libctest.so.1: cannot open shared object file: No such file or directory

    我們可以將所需庫的路徑加入到系統路徑中,有三種方法可以完成:

    A.在/etc/ld.so.conf中加入所在路徑,然后執行ldconfig配置鏈接器運行時綁定配置。你也可以創建一個文件,將路徑寫入,然后使用ldconfig –f filename將配置寫入。
    B.修改LD_LIBRARY_PATH環境變量(Linux下,AIX下為LIBPATH),在其中添加路徑。若你直接在.bashrc文件中配置則重啟后不失效,否則在shell中設置重啟后失效。
    我們使用A方法中的-f選項:

    gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ vi libctest.conf
    gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ sudo ldconfig -f libctest.conf
    gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ ./prog
    Valx=5
    gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ ldd prog
        linux-gate.so.1 =>  (0x00f6f000)
        libctest.so.1 => /usr/local/lib/libctest.so.1 (0x005d9000)
        libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0x00718000)
        /lib/ld-linux.so.2 (0x001e6000)

    其中libctest.conf中寫入路徑:/usr/local/lib。程序運行正常。

    4.動態加載庫的使用

    動態加載庫是在非程序啟動時動態加載進入程序的庫,這對于實現插件或動態模塊有很大的幫助。在Linux中,動態加載庫的形式并不特殊,它使用上述兩種程序庫,使用提供的API在程序運行時動態加載。注意,在不同平臺上動態加載庫的API并不相同,所以可能會有移植問題出現。

    我們可以通過nm命令先查看一下我們創建的庫里面有哪些symbol(可以理解為函數方法)供我們使用:

    gnuhpc@gnuhpc-desktop:~/MyCode/lib$ nm /usr/local/lib/libctest.so
    00001f18 a _DYNAMIC
    00001ff4 a _GLOBAL_OFFSET_TABLE_
             w _Jv_RegisterClasses
    00001f08 d __CTOR_END__
    00001f04 d __CTOR_LIST__
    00001f10 d __DTOR_END__
    00001f0c d __DTOR_LIST__
    000005a0 r __FRAME_END__
    00001f14 d __JCR_END__
    00001f14 d __JCR_LIST__
    00002014 A __bss_start
             w __cxa_finalize@@GLIBC_2.1.3
    00000540 t __do_global_ctors_aux
    00000420 t __do_global_dtors_aux
    00002010 d __dso_handle
             w __gmon_start__
    000004d7 t __i686.get_pc_thunk.bx
    00002014 A _edata
    0000201c A _end
    00000578 T _fini
    000003a0 T _init
    00002014 b completed.7021
    000004dc T ctest1
    000004ec T ctest2
    00002018 b dtor_idx.7023
    000004a0 t frame_dummy
    000004fc T main
             U printf@@GLIBC_2.0

    這個命令對靜態庫和共享庫都支持,第二列為symbol類型,小寫字母表示符號是本地的,大寫字母表示符號是全局(外部)的,幾個常見的字母含義如下:T為代碼段普通定義,D為已初始化數據段,B為未初始化數據段,U為未定義(用到該符號但是沒有在該庫中定義)。

    我們創建ctest.h:

      1: #ifndef CTEST_H
      2: #define CTEST_H
      3:
      4: #ifdef __cplusplus
      5: extern "C" {
      6: #endif
      7:
      8: void ctest1(int *);
      9: void ctest2(int *);
     10:
     11: #ifdef __cplusplus
     12: }
     13: #endif
     14:
     15: #endif這里使用extern C是為了使得該庫既可以用于C語言又可以用于C++。

    我們動態加載庫進來:progdl.c

      1: #include
      2: #include
      3: #include "ctest.h"
      4:
      5: int main(int argc, char **argv)
      6: {
      7:    void *lib_handle;
      8:    double (*fn)(int *);
      9:    int x;
     10:    char *error;
     11:
     12:    lib_handle = dlopen("/usr/local/lib/libctest.so", RTLD_LAZY);
     13:    if (!lib_handle)
     14:    {
     15:       fprintf(stderr, "%s\n", dlerror());
     16:       exit(1);
     17:    }
     18:
     19:    fn = dlsym(lib_handle, "ctest1");
     20:    if ((error = dlerror()) != NULL) 
     21:    {
     22:       fprintf(stderr, "%s\n", error);
     23:       exit(1);
     24:    }
     25:
     26:    (*fn)(&x);
     27:    printf("Valx=%d\n",x);
     28:
     29:    dlclose(lib_handle);
     30:    return 0;
     31: }里面的方法解釋如下:

    void * dlopen(const char *filename, int flag); 
    若filename為絕對路徑,那么dlopen就會試圖打開它而不搜索相關路徑,否則就現在環境變量LD_LIBRARY_PATH處搜索,然后在/etc/ld.so.cache以及/lib和/usr/lib搜索。flag我們只解釋兩個常用的選項:若為RTLD_LAZY則表示在動態庫執行時解決未定義符號問題,而RTLD_NOW則表示在dlopen返回前解決未定義符號問題。當你調試時你應該用RTLD_NOW,這個時候若存在未解決的引用程序還可以繼續進行。另外,RTLD_NOW選項可能會使打開庫的這個操作稍微慢一點,但是以后尋找函數時就會快一點。注意,若程序庫相互依賴則應該按依賴順序依次載入,比如X依賴Y,那么要先載入Y然后再載入X。返回的是一個句柄,若失敗則返回null.

    char *dlerror(void);
    報告任何上一次對加載庫操作的錯誤。兩次調用期間若有操作錯誤則第二次會報告, 否則第二次則返回null——它報告完錯誤就等待下一個錯誤的發生,上一次錯誤的情況一旦報告就不再提及。

    void *dlsym(void *handle, const char *symbol);
    尋找對應symbol的函數方法,handle就是dlopen返回的句柄。一般如下使用:
      1:  dlerror(); /* clear error code */
      2:  s = (actual_type) dlsym(handle, symbol_being_searched_for);
      3:  if ((err = dlerror()) != NULL) {
      4:   /* handle error, the symbol wasn't found */
      5:  } else {
      6:   /* symbol found, its value is in s */
      7:  }int dlclose(void *handle);
    關閉一個動態加載庫。當一個動態庫被加載多次時,你需要用同樣次數dlclose該動態庫才可以deallocated.

    我們編譯該代碼gcc -g -rdynamic -o progdl progdl.c -ldl,即可得到可執行文件(其中-g選項是為了gdb調試所用),其中的庫為動態加載后又關閉的。我們使用gdb看一下代碼:

    (gdb) b main
    Breakpoint 1 at 0x804878d: file progdl.c, line 12.
    (gdb) r
    Starting program: /home/gnuhpc/MyCode/lib/dynamic/progdl

    Breakpoint 1, main (argc=1, argv=0xbffff4a4) at progdl.c:12
    12       lib_handle = dlopen("/usr/local/lib/libctest.so", RTLD_LAZY);
    (gdb) f
    #0  main (argc=1, argv=0xbffff4a4) at progdl.c:12
    12       lib_handle = dlopen("/usr/local/lib/libctest.so", RTLD_LAZY);
    (gdb) s
    13       if (!lib_handle)
    (gdb) n
    19       fn = dlsym(lib_handle, "ctest1");
    (gdb)
    20       if ((error = dlerror()) != NULL) 
    (gdb)
    26       (*fn)(&x);
    (gdb)
    27       printf("Valx=%d\n",x);
    (gdb) p x
    $1 = 5
    (gdb) p fn
    $2 = (double (*)(int *)) 0x28c4dc

    可以看到fn獲得了ctest1的地址。

     

    參考文獻:

    http://www.yolinux.com/TUTORIALS/LibraryArchives-StaticAndDynamic.html

    http://www.linuxjournal.com/article/3687

    http://www.dwheeler.com/program-library/Program-Library-HOWTO/

     

    本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/gnuhpc/archive/2010/12/20/6086143.aspx

    posted on 2011-01-20 00:21 何克勤 閱讀(404) 評論(0)  編輯  收藏

    只有注冊用戶登錄后才能發表評論。


    網站導航:
     
    主站蜘蛛池模板: 亚洲国产成人精品激情| 久久久久亚洲AV无码专区体验| 亚洲H在线播放在线观看H| 久9这里精品免费视频| 国产亚洲无线码一区二区| 国产无遮挡色视频免费观看性色| 国产午夜免费秋霞影院| 免费在线观看亚洲| 亚洲不卡AV影片在线播放| 免费观看亚洲人成网站| 中文字幕不卡亚洲| 成全视频高清免费观看电视剧| 亚洲精品乱码久久久久66| 久久国产精品免费看| 亚洲精品美女视频| 啦啦啦中文在线观看电视剧免费版| 亚洲午夜无码毛片av久久京东热| 成人午夜18免费看| 免费亚洲视频在线观看| 青青草原亚洲视频| 91视频免费网址| 99亚偷拍自图区亚洲| 无码不卡亚洲成?人片| 99re8这里有精品热视频免费| 亚洲av日韩av不卡在线观看| 综合在线免费视频| 久久久久亚洲国产AV麻豆| 黑人大战亚洲人精品一区 | 婷婷亚洲综合一区二区| www国产亚洲精品久久久日本| 国产精品免费看久久久香蕉| 亚洲成人激情在线| 成人毛片视频免费网站观看| 一级毛片免费播放男男| 亚洲国产第一页www| 成人免费看黄20分钟| 中文字幕免费观看视频| 亚洲1234区乱码| 国产精品亚洲二区在线观看| 91热成人精品国产免费| 国产亚洲福利精品一区二区|