C/C++运行时库 解释
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信息,...。
總結
以上是生活随笔為你收集整理的C/C++运行时库 解释的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C运行时库和标准C++库
- 下一篇: C++指针数组、数组指针、数组名及二维数