您好,登錄后才能下訂單哦!
本篇內容主要講解“Linux系統so文件內容有哪些”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“Linux系統so文件內容有哪些”吧!
也是ELF格式文件,共享庫(動態庫),類似于DLL。節約資源,加快速度,代碼升級簡化。 知道這么多就夠了,實用主義。等有了印象再研究原理。
先寫一個C文件:s.c
#include int count; void out_msg(const char *m) {//2秒鐘輸出1次信息,并計數 for(;;) {printf("%s %d\n", m, ++count); sleep(2);} }
編譯:得到輸出文件libs.o gcc -fPIC -g -c s.c -o libs.o
與位置無關代碼(Position-Independent Code),則產生的代碼中,沒有絕對地址,全部使用相對地址,故而代碼可以被加載器加載到內存的任意 位置,都可以正確的執行。這正是共享庫所要求的,共享庫被加載時,在內存的位置不是固定的。
: -fPIC作用于編譯階段,告訴編譯器產生: 令 gcc 生成調試信息,該選項可以利用操作系統的“原生格式(native format)”生成調試信息。GDB 可以直接利用這個信息,其它調試器也可以使用這個調試信息
-c: 僅執行編譯操作,不進行連接操作。 -o: 指定生成的輸出文件名稱
注意!-c,-o不是指.c文件和.o文件!!
得到輸出文件libs.so gcc -g -shared -Wl,-soname,libs.so -o libs.so libs.o -lc
上述語句中 libs.o是輸入文件
-shared:
-Wl: 注意第二個字母是小寫的L,不是I
:
soname的關鍵功能是它提供了兼容性的標準:
當要升級系統中的一個庫時,并且新庫的soname和老庫的soname一樣,用舊庫鏈接生成的程序使用新庫依然能正常運行。這個特性使得在Linux下,升級使得共享庫的程序和定位錯誤變得十分容易。
在Linux中,應用程序通過使用soname,來指定所希望庫的版本,庫作者可以通過保留或改變soname來聲明,哪些版本是兼容的,這使得程序員擺脫了共享庫版本沖突問題的困擾。
-lc:
-l 是直接加上某庫的名稱,如-lc是libc庫 -L 是庫的路徑,搜索的時候優先在-L目錄下搜索
一個頭文件:s.h
#ifndef _MY_SO_HEADER_ #define _MY_SO_HEADER_ void out_msg(const char *m); #endif
再來一個C文件來引用這個庫中的函數:ts.c
#include #include "s.h" int main(int argc, char** argv) { printf("TS Main\n"); out_msg("TS "); sleep(5); //這句話可以注釋掉,在第4節的時候打開就可以。 printf("TS Quit\n"); }
編譯鏈接這個文件:得到輸出文件ts gcc -g ts.c -o ts -L. -ls
執行./ts,嗯:成功了。。。還差點 得到了ts:error while loading shared libraries: libs.so: cannot open shared object file: No such file or directory 系統不能找到我們自己定義的libs.so,那么告訴他,修改變量LD_LIBRARY_PATH,為了方便,寫個腳本:e(文件名就叫e,懶得弄長了)
export LD_LIBRARY_PATH=${pwd}:${LD_LIBRARY_PATH} ./ts
執行:./e & 屏幕上就開始不停有信息輸出了,當然TS Quit你是看不到的,前面是個死循環,后面會用到這句
———————-
& 放在啟動參數后面表示設置此進程為后臺進程。默認情況下,進程是前臺進程,這時就把Shell給占據了,我們無法進行其他操作,對于那些沒有交互的進程,很多時候,我們希望將其在后臺啟動,可以在啟動參數的時候加一個’&’實現這個目的。
———————-
3.地址空間,以及線程安全: 如果這樣: ./e &開始執行后,稍微等待一下然后再 ./e&, 這個時候屏幕信息會怎么樣呢?全局變量count會怎么變化? 會是兩個進程交叉輸出信息,并且各自的count互不干擾,雖然他們引用了同一個so文件。 也就是說只有代碼是否線程安全一說,沒有代碼是否是進程安全這一說法。
下面的還沒細看,汗
4.庫的初始化,解析: windows下的動態庫加載,卸載都會有初始化函數以及卸載函數來完成庫的初始化以及資源回收,linux當然也可以實現。 ELF文件本身執行時就會執行一個init()函數以及fini()函數來完成這個,我們只要把自己的函數能讓系統在這個時候執行 就可以了。 修改我們前面的s.c文件:
#include void my_init(void) __attribute__((constructor)); //告訴gcc把這個函數扔到init section void my_fini(void) __attribute__((destructor)); //告訴gcc把這個函數扔到fini section void out_msg(const char *m) { printf(" Ok!\n"); } int i; //仍然是個計數器 void my_init(void) { printf("Init ... ... %d\n", ++i); } void my_fini(void) { printf("Fini ... ... %d\n", ++i); }
重新制作 libs.so,ts本是不用重新編譯了,代碼維護升級方便很多。 然后執行: ./e & 可以看到屏幕輸出:(不完整信息,只是順序一樣) Init Main OK Quit Fini 可以看到我們自己定義的初始化函數以及解析函數都被執行了,而且是在最前面以及最后面。 如果s.c中的sleep(5)沒有注釋掉,那么有機會: ./e& ./e&連續執行兩次,那么初始化函數和解析函數也會執行兩次,雖然系統只加載了一次libs.so。 如果sleep時候kill 掉后臺進程,那么解析函數不會被執行。
5.使用我們自己庫里的函數替換系統函數: 創建一個新的文件b.c:我們要替換系統函數malloc以及free(可以自己寫個內存泄露檢測工具了)
#include void* malloc(int size) { printf("My malloc\n"); return NULL; } void free(void* ad) { printf("My free\n"); }
老規矩,編譯鏈接成一個so文件:得到libb.so gcc -fPIC -g -c b.c -o libb.o gcc -g -shared -Wl,-soname,libb.so -o libb.so -lc 修改s.c:重新生成libs.so
void out_msg() { int *p; p = (int*)malloc(100); free(p); printf("Stop Ok!\n"); }
修改腳本文件e:
export LD_PRELOAD=${pwd}libb.so:${LD_PRELOAD} export LD_LIBRARY_PATH=${pwd}:${LD_LIBRARY_PATH} ./ts
關鍵就在LD_PRELOAD上了,這個路徑指定的so將在所有的so之前加載,并且符號會覆蓋后面加載的so文件中的符號。如果可執行文件的權限不合適(SID),這個變量會被忽略。 執行:./e & 嗯,可以看到我們的malloc,free工作了。
到此,相信大家對“Linux系統so文件內容有哪些”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。