資源描述:
《Linux多線程編程問(wèn)題》由會(huì)員上傳分享,免費(fèi)在線閱讀,更多相關(guān)內(nèi)容在教育資源-天天文庫(kù)。
1、Linux多線程編程問(wèn)題1重入問(wèn)題傳統(tǒng)的UNIX沒(méi)有太多考慮線程問(wèn)題,庫(kù)函數(shù)里過(guò)多使用了全局和靜態(tài)數(shù)據(jù),導(dǎo)致嚴(yán)重的線程重入問(wèn)題。1.1–D_REENTRANT/-pthread和errno的重入問(wèn)題。所先UNIX的系統(tǒng)調(diào)用被設(shè)計(jì)為出錯(cuò)返回-1,把錯(cuò)誤碼放在errno中(更簡(jiǎn)單而直接的方法應(yīng)該是程序直接返回錯(cuò)誤碼,或者通過(guò)幾個(gè)參數(shù)指針來(lái)返回)。由于線程共享所有的數(shù)據(jù)區(qū),而errno是一個(gè)全局的變量,這里產(chǎn)生了最糟糕的線程重入問(wèn)題。比如:do{bytes=recv(netfd,recvbuf,buflen,0);}while
2、(bytes!=-1&&errno!=EINTR);在上面的處理recv被信號(hào)打斷的程序里。如果這時(shí)連接被關(guān)閉,此時(shí)errno應(yīng)該不等于EINTR,如果別的線程正好設(shè)置errno為EINTR,這時(shí)程序就可能進(jìn)入死循環(huán)。其它的錯(cuò)誤碼處理也可能進(jìn)入不可預(yù)測(cè)的分支。在線程需求剛開(kāi)始時(shí),很多方面技術(shù)和標(biāo)準(zhǔn)(TLS)還不夠成熟,所以在為了解決這個(gè)重入問(wèn)題引入了一個(gè)解決方案,把errno定義為一個(gè)宏:externint*__errno_location(void);#defineerrno(*__errno_location())在
3、上面的方案里,訪問(wèn)errno之前先調(diào)用__errno_location()函數(shù),線程庫(kù)提供這個(gè)函數(shù),不同線程返回各自errno的地址,從而解決這個(gè)重入問(wèn)題。在編譯時(shí)加-D_REENTRANT就是啟用上面的宏,避免errno重入。另外-D_REENTRANT還影響一些stdio的函數(shù)。在較高版本的gcc里,有很多嵌入函數(shù)的優(yōu)化,比如把printf(“Hello”);優(yōu)化為puts(“hello”);之類的,有些優(yōu)化在多線程下有問(wèn)題。所以gcc引入了–pthread參數(shù),這個(gè)參數(shù)出了-D_REENTRANT外,還校正
4、一些針對(duì)多線程的優(yōu)化。因?yàn)楹晔蔷幾g時(shí)確定的,所以沒(méi)有加-D_REENTRANT編譯的程序和庫(kù)都有errno重入問(wèn)題,原則上都不能在線程環(huán)境下使用。不過(guò)在一般實(shí)現(xiàn)上主線程是直接使用全局errno變量的,也就是__errno_location()返回值為全局&errno,所以那些沒(méi)加-D_REENTRANT編譯的庫(kù)可以在主線程里使用。這里僅限于主線程,有其它且只有一個(gè)固定子線程使用也不行,因?yàn)樽泳€程使用的errno地址不是全局errno變量地址。對(duì)于一個(gè)純算法的庫(kù),不涉及到errno和stdio等等,有時(shí)不加_REENTRA
5、NT也是安全的,比如一個(gè)純粹的加密/解謎函數(shù)庫(kù)。比較簡(jiǎn)單的判斷一個(gè)庫(kù)是否有errno問(wèn)題是看看這個(gè)庫(kù)是使用了errno還是__errno_location():readelf-slibxxx.so
6、greperrno另外一個(gè)和errno類似的變量是DNS解析里用到的h_errno變量,這個(gè)變量的重入和處理與errno一樣。這個(gè)h_errno用于gethostbyXX這個(gè)系列的函數(shù)。1.1庫(kù)函數(shù)重入早期很多unix函數(shù)設(shè)計(jì)成返回靜態(tài)buffer。這些函數(shù)都是不能重入的。識(shí)別這些函數(shù)有幾個(gè)簡(jiǎn)單的規(guī)則:1.1.1stdio函數(shù)
7、是可以重入的。這是因?yàn)閟tdio函數(shù)入口都會(huì)調(diào)用flockfile()鎖定FILE。另外stdio也提供不鎖定(非重入)的函數(shù),這些函數(shù)以_unlock結(jié)尾,具體參見(jiàn)manunlocked_stdio。利用這些特性可以做到多個(gè)stdio的互斥操作。如:flockfile(fp);fwrite_unlocked(rec1,reclen1,1,fp);fwrite_unlocked(rec2,reclen2,1,fp);funlockfile(fp);1.1.2返回動(dòng)態(tài)分配數(shù)據(jù)的函數(shù),這些一般是可以重入的。這些函數(shù)的特點(diǎn)是返
8、回的指針需要顯式釋放,用free或者配對(duì)的釋放函數(shù)。如:getaddrinfo/freeaddrinfomalloc/strdup/calloc/freefopen/fdopen/popen/fcloseget_current_dir_name/freeasprintf/vasprintf/freegetline/getdelim/freeregcomp/regfree1.1.3函數(shù)返回一個(gè)和輸入?yún)?shù)無(wú)關(guān)的數(shù)據(jù),而且不需要free的大部分情況下是不可重入的。如gmtime,ntoa,gethostbyname…1.1.4
9、函數(shù)依賴一個(gè)全局?jǐn)?shù)據(jù),在多次或者多個(gè)函數(shù)間維護(hù)狀態(tài)的函數(shù)是不可重入的。如getpwent,rand…1.1.5帶有_r變體的函數(shù)都是不可重入的。這些函數(shù)大部分是上面兩類的。這些變體函數(shù)是可重入的代替版本。可以用下面命令查看glibc有多少這種函數(shù):readelf-s/lib/libc.so.6
10、grep_r@這些函數(shù)