C/C++執行時庫 解釋

licup123發表於2009-08-15

I. CRT

CRT(C/C++ Runtime Library)是支援C/C++執行的一系列函式和程式碼的總稱。雖然沒有一個很精確的定義,但是可以知道,你的main就是它負責呼叫的,你平時呼叫的諸如strlen、strtok、time、atoi之類的函式也是它提供的。我們以Microsoft Visual.NET 2003中所附帶的CRT為例。假設你的.NET 2003安裝在C:Program FilesMicrosoft Visual Studio .NET 2003中,那麼CRT的原始碼就在C:Program FilesMicrosoft Visual Studio .NET 2003Vc7crtsrc中。既然有了這些實現的原始碼,我們就可以找到一切解釋了。

II. _beginthread/_endthread

這個函式究竟做了什麼呢?它的程式碼在thread.c中。閱讀程式碼,可以看到它最終也是通過CreateThread來建立執行緒的,主要區別在於,它先分配了一個_tiddata,並且呼叫了_initptd來初始化這個分配了的指標。而這個指標最後會被傳遞到CRT的執行緒包裝函式_threadstart中,在那裡會把這個指標作為一個TLS(Thread Local Storage)儲存起來。然後_threadstart會呼叫我們傳入的執行緒函式,並且在那個函式退出後呼叫_endthread。這裡也可以看到,_threadstart用一個__try/__except塊把我們的函式包了起來,並且在發生異常的時候,呼叫exit退出。(_threadstart和endthread的程式碼都在thread.c中)
這個_tiddata是一個什麼樣的結構呢?它在mtdll.h中定義,它的成員被很多CRT函式所用到,譬如int _terrno,這是這個執行緒中的錯誤標誌;char* _token,strtok以來這個變數記錄跨函式呼叫的資訊,...。
那麼_endthread又做了些什麼呢?除了呼叫浮點的清除程式碼以外,它還呼叫了_freeptd來釋放和這個執行緒相關的tiddata。也就是說,在_beginthread裡面分配的這塊記憶體,以及線上程執行過程中其它CRT函式中分配並且記錄在這個記憶體結構中的記憶體,在這裡被釋放了。
通過上面的程式碼,我們可以看到,如果我使用_beginthread函式建立了執行緒,它會為我建立好CRT函式需要的一切,並且最後無需我操心,就可以把清除工作做得很好,可能唯一需要注意的就是,如果需要提前終止執行緒,最好是呼叫_endthread或者是返回,而不要呼叫ExitThread,因為這可能造成記憶體釋放不完全。同時我們也可以看出,如果我們用CreateThread函式建立了執行緒,並且不對C執行庫進行呼叫(包括任何間接呼叫),就不必擔心什麼問題了。

III. CreateThread和CRT

或許有人會說,我用CreateThread建立執行緒以後,我也呼叫了C執行庫函式,並且也使用ExitThread退出了,可是我的程式執行得好好的,既沒有因為CRT沒有初始化而崩潰,也沒有因為忘記呼叫_endthread而發生記憶體洩漏,這是為什麼呢,讓我們繼續我們的CRT之旅。
假設我用CreateThread建立了一個執行緒,我呼叫strtok函式來進行字串處理,這個函式肯定是需要某些額外的執行時支援的。strtok的原始碼在strtok.c中。從程式碼可見,在多執行緒情況下,strtok的第一句有效程式碼就是_ptiddata ptd = _getptd(),它通過這個來獲得當前的ptd。可是我們並沒有通過_beginthread來建立ptd,那麼一定是_getptd搗鬼了。開啟tidtable.c,可以看到_getptd的實現,果然,它先嚐試獲得當前的ptd,如果不能,就重新建立一個,因此,後續的CRT呼叫就安全了。可是這塊ptd最終又是誰釋放的呢?開啟dllcrt0.c,可以看到一個DllMain函式。在VC中,CRT既可以作為一個動態連結庫和主程式連結,也可以作為一個靜態庫和主程式連結,這個在Project Setting->Code Generations裡面可以選。當CRT作為DLL連結到主程式時,DllMain就是CRT DLL的入口。Windows的DllMain可以由四種原因呼叫:Process Attach/Process Detach/Thread Attach/Thread Detach,最後一個,也就是當執行緒函式退出後但是執行緒還沒有銷燬前,會在這個執行緒的上下文中用Thread Detach呼叫DllMain,這裡,CRT做了一個_freeptd(NULL),也就是說,如果有ptd,就free掉。所以說,恰巧沒有發生記憶體洩漏是因為你用的是動態連結的CRT。
於是我們得出了一個更精確的結論,如果我沒有使用那些會使用_getptd的CRT函式,使用CreateThread就是安全的。

IV. 使用ptd的函式

那麼,究竟那些函式使用了_getptd呢?很多!在CRT目錄下搜尋_getptd,你會發覺很多意想不到的函式都用到了它,除了strtok、rand這類需要保持狀態的,還有所有的字串相關函式,因為它們要用到ptd中的locale資訊;所有的mbcs函式,因為它們要用到ptd中的mbcs資訊,...。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10697500/viewspace-612404/,如需轉載,請註明出處,否則將追究法律責任。

相關文章