CLR簡(jiǎn)介(三)

“托管代碼”概念

能夠執(zhí)行額外記錄一般在“幾乎任何時(shí)刻”報(bào)告其正在使用的有效GC引用的代碼,就稱做 托管代碼 (因?yàn)槠浔籆LR“管理”)。不能實(shí)現(xiàn)這個(gè)目標(biāo)的代碼叫 非托管代碼。因此在CLR之前存在的代碼都是非托管代碼,特別來(lái)說(shuō),所有操作系統(tǒng)的代碼都是非托管的。

堆棧展開的問(wèn)題

由于托管代碼需要用到操作系統(tǒng)的服務(wù),所有有時(shí)托管代碼需要調(diào)用非托管代碼。相應(yīng)的,托管代碼最初是操作系統(tǒng)啟動(dòng)的,因此有時(shí)非托管代碼也會(huì)調(diào)用托管代碼。因此,當(dāng)你在任意位置中斷托管程序,堆棧上混合了由托管代碼和非托管代碼創(chuàng)建的幀。

非托管代碼的堆棧幀對(duì)其上運(yùn)行的程序 沒有 要求。即一般不要求 展開 (非托管堆棧幀)來(lái)找到它的調(diào)用者。這意味著當(dāng)程序在非托管函數(shù)中斷時(shí),一般[1] 是沒有辦法找到它的調(diào)用函數(shù)的。只能在調(diào)試器里才能做到,這是因?yàn)樵诜?hào)(PDB)文件里保存了額外信息。但這些信息不保證一直存在(這就是為什么在調(diào)試器里無(wú)法獲取準(zhǔn)確堆棧信息的原因)。這在托管代碼里是很大的問(wèn)題,因?yàn)樵跓o(wú)法展開的堆棧里可能包含托管代碼幀(它包含了需要收集的GC引用)。

托管代碼有一些額外需求:不僅是在運(yùn)行過(guò)程中需要跟蹤所有的GC引用,而且還需要能展開它的調(diào)用函數(shù)。另外,無(wú)論何時(shí)從托管代碼到非托管代碼(或發(fā)過(guò)來(lái))的過(guò)渡,托管代碼都需要做一些額外的記錄來(lái)避免非托管代碼無(wú)法展開堆棧帶來(lái)的影響。實(shí)際上,托管代碼在堆棧里將托管代碼幀鏈接起來(lái)了。因此,即使在無(wú)法利用額外信息展開非托管代碼堆棧幀的情況下,還是能在堆棧上找到托管代碼塊并枚舉托管代碼幀。

[1] 最近的平臺(tái)ABI(應(yīng)用程序二進(jìn)制接口 application binary interfaces)定義了編碼這些信息的約定,但其不是一個(gè)所有代碼都必須遵循的嚴(yán)格規(guī)范。

托管代碼的“世界”

結(jié)果就是進(jìn)出托管代碼的每次過(guò)渡都要做特殊的記錄。托管代碼只能在CLR理解的“世界”。這兩個(gè)世界是非常不同的(在任何時(shí)刻,代碼要么在 托管世界,要么在 非托管世界)。而且,因?yàn)樵贑LR格式里定義了托管代碼的執(zhí)行(即 [通用中間語(yǔ)言]cil-spec),并由CLR在原生硬件上解釋執(zhí)行,CLR對(duì)運(yùn)行情況有 非常多 的控制。例如,CRL可以更改從一個(gè)對(duì)象里獲取字段的值或調(diào)用一個(gè)函數(shù)的意思。實(shí)際上,CLR在創(chuàng)建MarshalByReference對(duì)象時(shí)就是這么做的。它們看起來(lái)是普通的本地對(duì)象,但實(shí)際上存在于另外一臺(tái)機(jī)器。簡(jiǎn)單來(lái)說(shuō),CLR的托管世界里有很多 運(yùn)行時(shí)鉤子 來(lái)支持后續(xù)章節(jié)要介紹的強(qiáng)大功能。

另外,托管代碼還有一個(gè)很重要但不是很明顯的衍生物。在非托管代碼里,GC指針是不被允許的(因?yàn)槠洳豢杀桓櫍?,在托管和非托管之間的切換有一個(gè)記錄的成本。這意味著雖然你 可以 在托管代碼里調(diào)用任意的非托管方法,但過(guò)程通常不是很方便。非托管代碼無(wú)法使用GC對(duì)象作為其參數(shù)或者返回值,也就是說(shuō)這些非托管方法創(chuàng)建和使用的任何“對(duì)象”或“對(duì)象引用”都需要顯式釋放。這實(shí)在是個(gè)悲劇。因?yàn)檫@些API無(wú)法享受到諸如異?;蚶^承這樣的CLR的功能的益處,這將會(huì)導(dǎo)致與托管代碼交互時(shí)“不匹配”的用戶體驗(yàn)。

其結(jié)果就是大部分非托管接口在暴露給托管代碼開發(fā)者時(shí)是 封裝的。舉個(gè)例子,當(dāng)訪問(wèn)文件時(shí),一般不使用操作系統(tǒng)提供的Win32 CreateFile 函數(shù),而是用封裝了其的System.IO.File托管代碼類。實(shí)際上直接使用非托管API的地方非常少。

雖然這種封裝在一些地方看起來(lái)“差勁”(更多的代碼不見得做的更多),實(shí)際上它還是增加了一些價(jià)值的。請(qǐng)記住直接暴露非托管接口總是可能的;但我們選擇封裝這些功能,為什么?因?yàn)檎麄€(gè)運(yùn)行時(shí)設(shè)計(jì)的首要目標(biāo)是使編程更簡(jiǎn)單,而且一般來(lái)說(shuō)非托管代碼不是足夠簡(jiǎn)單。通常來(lái)說(shuō),非托管接口一開始就不是為了使用簡(jiǎn)單而設(shè)計(jì)的,而是為完整性優(yōu)化的。當(dāng)你看到CreateFile或者CreateProcess函數(shù)的參數(shù)列表時(shí),很難將其歸類到簡(jiǎn)單里。幸運(yùn)的是,這些接口進(jìn)入托管世界“整了次容”,雖然過(guò)程很“沒技術(shù)含量”(無(wú)非就是重命名,簡(jiǎn)化,重組其功能),但非常有用。為CLR編寫的一個(gè)非常重要的文檔就是 [Framework Design Guidelines][fx-design-guidelines]。這本800+頁(yè)的文檔詳細(xì)描述了創(chuàng)建新的托管代碼類庫(kù)的最佳實(shí)踐。

到這里,我們分析了托管代碼和非托管代碼里兩個(gè)重要不同:

  1. 高科技:代碼在兩個(gè)不同的世界里運(yùn)行,而CLR在程序運(yùn)行時(shí)的各個(gè)方面進(jìn)行良好的把控(甚至到單個(gè)指令級(jí)別),而且CLR可以檢測(cè)什么時(shí)候進(jìn)入或退出托管代碼的運(yùn)行。這點(diǎn)使很多有用的功能變得可能。
  2. 低技術(shù)含量:在托管和非托管代碼之間有切換成本,而且非托管代碼無(wú)法使用GC對(duì)象這點(diǎn)事實(shí)鼓勵(lì)用facade模式封裝非托管代碼。即通過(guò)遵循一系列的命名和設(shè)計(jì)指南來(lái)達(dá)到一定程度的一致性和可發(fā)現(xiàn)性來(lái)“整容”并簡(jiǎn)化(操作系統(tǒng))接口。

上面兩個(gè)特性對(duì)于托管代碼的成功都非常重要。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 托管 vs. 原生線程 托管代碼在“托管線程”上執(zhí)行,(托管線程)與操作系統(tǒng)提供的原生線程不同。原生線程是在物理機(jī)...
    懿民閱讀 786評(píng)論 0 5
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評(píng)論 19 139
  • 《騰訊桌球:客戶端總結(jié)》 本次分享總結(jié),起源于騰訊桌球項(xiàng)目,但是不僅僅限于項(xiàng)目本身。雖然基于Unity3D,很多東...
    吳秦閱讀 25,251評(píng)論 12 143
  • 1. [C#語(yǔ)言基礎(chǔ)]請(qǐng)簡(jiǎn)述拆箱和裝箱。 答: 裝箱操作: 值類型隱式轉(zhuǎn)換為object類型或由此值類型實(shí)現(xiàn)的任何...
    胤醚貔貅閱讀 4,978評(píng)論 1 28
  • 阿發(fā)和阿花是兄妹,兩個(gè)人除了白天各打一份工,晚上還經(jīng)營(yíng)著一間餛飩鋪?zhàn)印?鋪?zhàn)娱_在繁華的商業(yè)街上,五六平方,一般晚上...
    任真閱讀 661評(píng)論 18 34

友情鏈接更多精彩內(nèi)容