溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Linux系統(tǒng)so文件內(nèi)容有哪些

發(fā)布時(shí)間:2022-01-26 10:25:40 來源:億速云 閱讀:336 作者:zzz 欄目:開發(fā)技術(shù)

本篇內(nèi)容主要講解“Linux系統(tǒng)so文件內(nèi)容有哪些”,感興趣的朋友不妨來看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“Linux系統(tǒng)so文件內(nèi)容有哪些”吧!

Linux系統(tǒng)so文件內(nèi)容有哪些

1.so文件簡(jiǎn)介:

也是ELF格式文件,共享庫(動(dòng)態(tài)庫),類似于DLL。節(jié)約資源,加快速度,代碼升級(jí)簡(jiǎn)化。 知道這么多就夠了,實(shí)用主義。等有了印象再研究原理。

2.怎么生成以及使用一個(gè)so動(dòng)態(tài)庫文件?

先寫一個(gè)C文件:s.c

 #include 
 int count;  
 void out_msg(const char *m)  
 {//2秒鐘輸出1次信息,并計(jì)數(shù)  
      for(;;) {printf("%s %d\n", m, ++count); sleep(2);}  
 }

編譯:得到輸出文件libs.o gcc -fPIC -g -c s.c -o libs.o

-fPIC:  -fPIC作用于編譯階段,告訴編譯器產(chǎn)生與位置無關(guān)代碼(Position-Independent Code),則產(chǎn)生的代碼中,沒有絕對(duì)地址,全部使用相對(duì)地址,故而代碼可以被加載器加載到內(nèi)存的任意 位置,都可以正確的執(zhí)行。這正是共享庫所要求的,共享庫被加載時(shí),在內(nèi)存的位置不是固定的。

-g:  令 gcc 生成調(diào)試信息,該選項(xiàng)可以利用操作系統(tǒng)的“原生格式(native format)”生成調(diào)試信息。GDB 可以直接利用這個(gè)信息,其它調(diào)試器也可以使用這個(gè)調(diào)試信息

-c:  僅執(zhí)行編譯操作,不進(jìn)行連接操作。 -o:  指定生成的輸出文件名稱

注意!-c,-o是指.c文件和.o文件?。?/p>

得到輸出文件libs.so gcc -g -shared -Wl,-soname,libs.so -o libs.so libs.o -lc

上述語句中 libs.o是輸入文件

-shared:

-Wl: 注意第二個(gè)字母是小寫的L,不是I

-soname:

  soname的關(guān)鍵功能是它提供了兼容性的標(biāo)準(zhǔn):

  當(dāng)要升級(jí)系統(tǒng)中的一個(gè)庫時(shí),并且新庫的soname和老庫的soname一樣,用舊庫鏈接生成的程序使用新庫依然能正常運(yùn)行。這個(gè)特性使得在Linux下,升級(jí)使得共享庫的程序和定位錯(cuò)誤變得十分容易。

  在Linux中,應(yīng)用程序通過使用soname,來指定所希望庫的版本,庫作者可以通過保留或改變soname來聲明,哪些版本是兼容的,這使得程序員擺脫了共享庫版本沖突問題的困擾。

-lc:

  -l 是直接加上某庫的名稱,如-lc是libc庫 -L 是庫的路徑,搜索的時(shí)候優(yōu)先在-L目錄下搜索

一個(gè)頭文件:s.h

 #ifndef _MY_SO_HEADER_  
 #define _MY_SO_HEADER_  
 void out_msg(const char *m);  
 #endif

再來一個(gè)C文件來引用這個(gè)庫中的函數(shù):ts.c

  #include 
  #include "s.h"  
  int main(int argc, char** argv)  
  {  
     printf("TS Main\n");  
     out_msg("TS ");  
     sleep(5);  //這句話可以注釋掉,在第4節(jié)的時(shí)候打開就可以。  
     printf("TS Quit\n");  
  }

編譯鏈接這個(gè)文件:得到輸出文件ts gcc -g ts.c -o ts -L. -ls

執(zhí)行./ts,嗯:成功了。。。還差點(diǎn) 得到了ts:error while loading shared libraries: libs.so: cannot open shared object file: No such file or directory 系統(tǒng)不能找到我們自己定義的libs.so,那么告訴他,修改變量LD_LIBRARY_PATH,為了方便,寫個(gè)腳本:e(文件名就叫e,懶得弄長了)

 export LD_LIBRARY_PATH=${pwd}:${LD_LIBRARY_PATH}
 ./ts

執(zhí)行:./e & 屏幕上就開始不停有信息輸出了,當(dāng)然TS Quit你是看不到的,前面是個(gè)死循環(huán),后面會(huì)用到這句

———————-

& 放在啟動(dòng)參數(shù)后面表示設(shè)置此進(jìn)程為后臺(tái)進(jìn)程。默認(rèn)情況下,進(jìn)程是前臺(tái)進(jìn)程,這時(shí)就把Shell給占據(jù)了,我們無法進(jìn)行其他操作,對(duì)于那些沒有交互的進(jìn)程,很多時(shí)候,我們希望將其在后臺(tái)啟動(dòng),可以在啟動(dòng)參數(shù)的時(shí)候加一個(gè)’&’實(shí)現(xiàn)這個(gè)目的。

———————-

3.地址空間,以及線程安全: 如果這樣: ./e &開始執(zhí)行后,稍微等待一下然后再 ./e&, 這個(gè)時(shí)候屏幕信息會(huì)怎么樣呢?全局變量count會(huì)怎么變化? 會(huì)是兩個(gè)進(jìn)程交叉輸出信息,并且各自的count互不干擾,雖然他們引用了同一個(gè)so文件。 也就是說只有代碼是否線程安全一說,沒有代碼是否是進(jìn)程安全這一說法。

下面的還沒細(xì)看,汗

4.庫的初始化,解析: windows下的動(dòng)態(tài)庫加載,卸載都會(huì)有初始化函數(shù)以及卸載函數(shù)來完成庫的初始化以及資源回收,linux當(dāng)然也可以實(shí)現(xiàn)。 ELF文件本身執(zhí)行時(shí)就會(huì)執(zhí)行一個(gè)init()函數(shù)以及fini()函數(shù)來完成這個(gè),我們只要把自己的函數(shù)能讓系統(tǒng)在這個(gè)時(shí)候執(zhí)行 就可以了。 修改我們前面的s.c文件:

 #include 
  void my_init(void) __attribute__((constructor)); //告訴gcc把這個(gè)函數(shù)扔到init section  
  void my_fini(void) __attribute__((destructor));  //告訴gcc把這個(gè)函數(shù)扔到fini section  
  void out_msg(const char *m)  
  {  
   printf(" Ok!\n");  
  }  
  int i; //仍然是個(gè)計(jì)數(shù)器  
  void my_init(void)  
  {  
   printf("Init ... ... %d\n", ++i);  
  }  
  void my_fini(void)  
  {  
   printf("Fini ... ... %d\n", ++i);  
  }

重新制作 libs.so,ts本是不用重新編譯了,代碼維護(hù)升級(jí)方便很多。 然后執(zhí)行: ./e & 可以看到屏幕輸出:(不完整信息,只是順序一樣) Init Main OK Quit Fini 可以看到我們自己定義的初始化函數(shù)以及解析函數(shù)都被執(zhí)行了,而且是在最前面以及最后面。 如果s.c中的sleep(5)沒有注釋掉,那么有機(jī)會(huì): ./e& ./e&連續(xù)執(zhí)行兩次,那么初始化函數(shù)和解析函數(shù)也會(huì)執(zhí)行兩次,雖然系統(tǒng)只加載了一次libs.so。 如果sleep時(shí)候kill 掉后臺(tái)進(jìn)程,那么解析函數(shù)不會(huì)被執(zhí)行。

5.使用我們自己庫里的函數(shù)替換系統(tǒng)函數(shù): 創(chuàng)建一個(gè)新的文件b.c:我們要替換系統(tǒng)函數(shù)malloc以及free(可以自己寫個(gè)內(nèi)存泄露檢測(cè)工具了)

 #include 
  void* malloc(int size)  
  {  
   printf("My malloc\n");  
   return NULL;  
  }  
  void free(void* ad)  
  {  
   printf("My free\n");  
  }

老規(guī)矩,編譯鏈接成一個(gè)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

關(guān)鍵就在LD_PRELOAD上了,這個(gè)路徑指定的so將在所有的so之前加載,并且符號(hào)會(huì)覆蓋后面加載的so文件中的符號(hào)。如果可執(zhí)行文件的權(quán)限不合適(SID),這個(gè)變量會(huì)被忽略。 執(zhí)行:./e & 嗯,可以看到我們的malloc,free工作了。

到此,相信大家對(duì)“Linux系統(tǒng)so文件內(nèi)容有哪些”有了更深的了解,不妨來實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI