編譯程序加不加 -lpthread 的區別【轉】


轉自:http://www.cnblogs.com/Swartz/articles/3939382.html

作者:Lokki 出處:http://www.cnblogs.com/Swartz/ 歡迎轉載,也請保留這段聲明。謝謝!

 

討論貼


 

最近在CSDN上看到一個帖子在討論 進程間共享的Posix mutex的鎖定狀態能否被子進程繼承?,其中4樓的帖子給出了一個測試局部mutex能否被繼承的例子:

復制代碼
 1 #include <pthread.h>
 2 #include <stdio.h>  3 #include <stdlib.h>  4 #include <unistd.h>  5  6 int main(void)  7 {  8  pid_t pid;  9  pthread_mutex_t mut; 10 11 pthread_mutex_init(&mut, NULL); 12 13 printf("lock\n"); 14 pthread_mutex_lock(&mut); 15 16 printf("fork\n"); 17 pid = fork(); 18 if( pid == 0 ) // 子進程嘗試鎖定 19  { 20 printf("child: lock\n"); 21 pthread_mutex_lock(&mut); 22 printf("child: over\n"); 23 24 exit(0); 25  } 26 27 pthread_mutex_destroy(&mut); 28 return(0); 29 }
復制代碼

 在之后的樓層討論,大家發現在編譯以上代碼時候加 -lpthread 和不加 -lpthread 得出了截然不同的結果。這是為什么呢?

1 gcc -o test1 main.c -lpthread 2 gcc -o test2 main.c

 

本文主要討論動態鏈接編譯的方式。

 

為什么加不加 -lpthread 都可以編譯通過且成功執行


 

由於程序正確地添加了頭文件 pthread.h,所以在編譯的鏈接階段之前,加不加 -lpthread 生成的目標文件是沒有區別的,區別在於編譯階段的鏈接過程。程序里面的pthread_mutex_init pthread_mutex_lock 等函數(事實上除了main函數之外的函數都是)是未定義的符號。編譯器需要在鏈接期間的正確地解析未定義符號的地址。那么對於test2中的pthread_mutex_lock函數,連接器能正確解析到嗎?我們先來看一下test1和test2都鏈接了哪些動態庫(注意鏈接庫的順序)。

很顯然,test2鏈接的動態庫里面沒有pthread。我們看一下libc.so里面的符號是不是有pthread_mutex_lock函數。

原來在libc.so庫里面有這些函數,這樣test2就可以正確編譯執行了。

 

為什么程序運行的結果不一樣?


 

在我們執行兩個程序之后得到以下結果:

顯然,test1的結果表明了局部mutex的狀態可以被子進程繼承,test2的結果卻恰恰相反。這是因為test1調用的pthread_mutex_*函數是來自於pthread庫,test2調用的函數來自libc.so庫。libc.so庫里面的pthread_mutex_*函數什么都沒有做,只是返回了0。這樣就會出現上述結果。

 

為什么libc要定義一些多線程的函數?


 

我們再次查看libc.so定義的pthread有關的函數時候,可以找到很多熟悉的函數(沒有pthread_create,為什么?)。

libc之所以這樣做是因為一些庫需要做到線程安全,但是自身卻不使用線程。試想,一個庫的函數需要使用mutex,當該函數調用時候需要lock和unlock。這樣在程序為單線程時候不必鏈接pthread庫這個函數仍然可以正常調用,事實上lock和unlock的動作不會有任何效果。在多線程時候,程序鏈接了pthread庫,這樣函數就可以正常地lock和unlock,實現線程安全的特性。

例如,根據posix標准,你每次調用fputc(ch, stream)都會有mutex的lock和unlock。

 

如何保證調用到有效的pthread函數?


 

1. 對於動態鏈接的程序

通過符號介入。符號介入是指:在動態鏈接時候,如果一個符號在多個庫里面有定義,那么連接器將使用它第一次找到的版本。看第一個圖,我們可以看到libpthread的鏈接順序要比libc靠前,默認情況下libc都排在編譯器鏈接順序的最后一位。這樣的話,如果程序沒有鏈接pthread庫,那么程序會調用libc的pthread函數。如果程序鏈接了pthread庫,由於pthread的鏈接順序總是排在libc之前,程序會調用pthread的函數。

 

2. 對於靜態鏈接的程序

之前我們討論的是動態鏈接的程序,其中涉及到了動態鏈接符號的解析,由於靜態鏈接程序沒有這個步驟,所以靜態鏈接程序不能通過這個來實現。我們可以看一下libc.a里面的pthread函數:

很明顯,pthread的相關函數都被定義為了弱符號,而這些函數在libpthread.a里面都被定義為強符號。這樣在程序鏈接pthread時候會調用pthread的強符號,沒有鏈接pthread時候會調用libc定義的函數。

 

3. 符號版本的作用

情況比較少,暫時忽略

 

后記


 

通過對現象的深入剖析,看到了libc庫的強大。事實上,libc除了pthread相關函數還實現了其他的一些函數,這些函數稱為stubs。他們不會實現實際的功能,只是一個占位符(place holder),當有真正的函數可用時候,他們就會讓出自己的位置。

GUN對stub的描述:

stub function is a function which cannot be implemented on a particular machine or operating system. Stub functions always return an error, and set errno to ENOSYS (Function not implemented). See Error Reporting. If you define a stub function, you must place the statement stub_warning(function), where function is the name of your function, after its definition. This causes the function to be listed in the installed <gnu/stubs.h>, and makes GNU ld warn when the function is used.

 

參考


 

  1. http://stackoverflow.com/questions/6266183/does-linking-an-lpthread-changes-application-behaviour-linux-glibc
  2. http://stackoverflow.com/questions/21092601/is-pthread-in-glibc-so-implemented-by-weak-symbol-to-provide-pthread-stub-functi
  3. http://stackoverflow.com/questions/11161462/why-glibc-and-pthread-library-both-defined-same-apis/11210463#11210463
  4. http://stackoverflow.com/questions/21092601/is-pthread-in-glibc-so-implemented-by-weak-symbol-to-provide-pthread-stub-functi
  5. http://www.gnu.org/software/libc/manual/html_node/Porting.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM