Linux共用庫(so)動態載入和升級

來源:互聯網
上載者:User

>>轉載請註明來源:飄零的代碼 piao2010 ’s blog,謝謝!^_^

>>本文連結地址:Linux共用庫(so)動態載入和升級

學習Linux共用庫動態載入緣於一個生產環境升級apache so檔案常見錯誤操作:apache在運行中直接cp覆蓋目標so檔案,一段時間後錯誤記錄檔裡面出現關鍵詞:Segmentation fault (段錯誤) ,一個個worker進程就這樣漸漸退出,最後無法處理HTTP請求。
首先瞭解一下共用庫的建立,源檔案test.c

?View Code C

 
#include<stdio.h>#include<unistd.h> void test1(void){    printf("This is do test1\n");    sleep(10);    printf("End of test1\n");} void test2(void){    printf("This is do test2\n");    sleep(10);    printf("End of test2\n");}

執行gcc -fPIC -shared -o libtest.so test.c 會產生共用庫檔案 libtest.so
參數含義:
-fPIC/-fpic: Compiler directive to output position independent code, a characteristic required by shared libraries. 建立共用庫必須的參數
-shared: Produce a shared object which can then be linked with other objects to form an executable.

然後使用共用庫:源檔案main2.c

?View Code C

 
#include <stdio.h> int main(){    test1();    test2();     return 0;}

動態庫連結:gcc -o main2 -L . -ltest main2.c 產生二進位程式main2
參數含義:
-L 指定動態庫目錄為目前的目錄
-l 指定動態庫名test,不要寫libtest.so

執行main2程式發現報錯:error while loading shared libraries: libtest.so: cannot open shared object file: No such file or directory
原因是共用庫不在系統預設的路徑裡面,可以在shell執行export LD_LIBRARY_PATH=./ 添加當前路徑或者在 /etc/ld.so.conf 增加路徑並ldconfig生效。
執行main2成功輸出:
This is do test1
End of test1
This is do test2
End of test2

接下來是主角:動態載入,源檔案main.c

?View Code C

 
#include <stdio.h>#include <dlfcn.h> /* 必須加這個標頭檔 */ int main(){   void *lib_handle;   void (*fn1)(void);   void (*fn2)(void);   char *error;    lib_handle = dlopen("libtest.so", RTLD_LAZY);   if (!lib_handle)    {      fprintf(stderr, "%s\n", dlerror());      return 1;   }    fn1 = dlsym(lib_handle, "test1");   if ((error = dlerror()) != NULL)     {      fprintf(stderr, "%s\n", error);      return 1;   }    fn1();    fn2 = dlsym(lib_handle, "test2");   if ((error = dlerror()) != NULL)     {      fprintf(stderr, "%s\n", error);      return 1;   }    fn2();    dlclose(lib_handle);    return 0;}

介面函數介紹:
(1) dlopen
函數原型:void *dlopen(const char *libname,int flag);
功能描述:dlopen必須在dlerror,dlsym和dlclose之前調用,表示要將庫裝載到記憶體,準備使用。
如果要裝載的庫依賴於其它庫,必須首先裝載依賴庫。如果dlopen操作失敗,返回NULL值;如果庫已經被裝載過,則dlopen會返回同樣的控制代碼。
參數中的libname一般是庫的全路徑,這樣dlopen會直接裝載該檔案;如果只是指定了庫名稱,在dlopen會按照下面的機制去搜尋:
a.根據環境變數LD_LIBRARY_PATH尋找
b.根據/etc/ld.so.cache尋找
c.尋找依次在/lib和/usr/lib目錄尋找。
flag參數表示處理未定義函數的方式,可以使用RTLD_LAZY或RTLD_NOW。RTLD_LAZY表示暫時不去處理未定義函數,先把庫裝載到內
存,等用到沒定義的函數再說;RTLD_NOW表示馬上檢查是否存在未定義的函數,若存在,則dlopen以失敗告終。

(2) dlerror
函數原型:char *dlerror(void);
功能描述:dlerror可以獲得最近一次dlopen,dlsym或dlclose操作的錯誤資訊,返回NULL表示無錯誤。dlerror在返回錯誤資訊的同時,也會清除錯誤資訊。

(3) dlsym
函數原型:void *dlsym(void *handle,const char *symbol);
功能描述:在dlopen之後,庫被裝載到記憶體。dlsym可以獲得指定函數(symbol)在記憶體中的位置(指標)。
如果找不到指定函數,則dlsym會返回NULL值。但判斷函數是否存在最好的方法是使用dlerror函數,

(4) dlclose
函數原型:int dlclose(void *);
功能描述:將已經裝載的庫控制代碼減一,如果控制代碼減至零,則該庫會被卸載。如果存在解構函式,則在dlclose之後,解構函式會被調用。

編譯gcc -o main main.c -ldl 產生二進位程式main,執行輸出
This is do test1
End of test1
This is do test2
End of test2

到這裡共用庫動態載入就介紹完了:)
最後類比一下升級so故障:
在執行main的時候,趁sleep期間cp 另外的so檔案覆蓋libtest.so,一會就出現Segmentation fault。
但是如果是mv 另外的so檔案覆蓋libtest.so,則無此問題,或者先rm libtest.so 再cp/mv 也不會有問題,因此升級方法就是這兩種,當然最好是先停應用再升級。至於原因,可以參考我前一篇部落格《Linux cp mv rm ln 命令對於 inode 和 dentry 的影響》。

12.5更新:
今天諮詢了維揚同學,可以用strace觀察程式運行期間的系統調用,發現有不少mmap操作:

?View Code C

 
省略前面open("/root/so/libtest.so", O_RDONLY)   = 3read(3, "\177ELF\1\1\1\3\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\240\3\0\0004\0\0\0"..., 512) = 512brk(0)                                  = 0x8227000brk(0x8248000)                          = 0x8248000fstat64(3, {st_dev=makedev(253, 0), st_ino=17559, st_mode=S_IFREG|0755, st_nlink=1, st_uid=0, st_gid=0, st_blksize=4096, st_blocks=16, st_size=4348, st_atime=2012/05/13-14:13:18, st_mtime=2012/05/13-14:13:01, st_ctime=2012/05/13-14:13:01}) = 0mmap2(NULL, 5772, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x6e6000mmap2(0x6e7000, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0) = 0x6e7000close(3)                                = 0munmap(0xb7753000, 15020)               = 0fstat64(1, {st_dev=makedev(0, 11), st_ino=3, st_mode=S_IFCHR|0620, st_nlink=1, st_uid=0, st_gid=5, st_blksize=1024, st_blocks=0, st_rdev=makedev(136, 0), st_atime=2012/05/13-14:56:03, st_mtime=2012/05/13-14:56:03, st_ctime=2012/05/13-14:53:31}) = 0mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7756000write(1, "This is do test1\n", 17)      = 17rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0nanosleep({10, 0}, 0xbfd63fe4)          = 0write(1, "End of test1\n", 13)          = 13write(1, "This is do test2\n", 17)      = 17rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0nanosleep({10, 0}, 0xbfd63fe4)          = 0--- SIGSEGV (Segmentation fault) @ 0 (0) ---+++ killed by SIGSEGV +++

SIGSEGV訊號估計和mmap唯讀映射之後寫入(覆蓋)檔案有關?
詳見續篇《為何cp覆蓋進程的動態庫(so)會導致coredump》。

參考資料:

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

http://hi.baidu.com/luoxsbupt/item/a9d346b7653a2771254b09bc

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.