http://youxiputao.com/articles/5064web
此次咱們翻譯了一篇Unity官方博客上的文章,原文題目爲AN INTRODUCTION TO IL2CPP INTERNALS ,做者是從事Unity軟件開發的Joshua Peterson。文章的看點在於,它是以IL2CPP內部開發人員的角度來說述的。後端
AN INTRODUCTION TO IL2CPP INTERNALS (做者:Joshua Peterson 翻譯:Bowie)數組
大約在一年之前,咱們寫了一篇博客討論Unity中腳本未來會是個什麼樣子,在那篇博客中咱們提到了嶄新的IL2CPP後端,並許諾其會爲Unity帶來更高效和更適合於各個平臺的虛擬機。在2015年的一月份,咱們正式發佈了第一個使用IL2CPP的平臺:iOS 64-bit。而隨着Unity 5的發佈,又帶給你們另外一個使用IL2CPP的平臺:WebGL。感謝咱們社區中用戶的大量寶貴的反饋,咱們在接下來的時間裏根據這些反饋得以更新IL2CPP,發佈補丁版本,從而持續的改進IL2CPP的編譯器和運行時庫。框架
咱們沒有中止改進IL2CPP的打算,可是在目前這個時間點上,咱們以爲能夠回過頭來抽出點時間告訴你們一些IL2CPP的內部工做機制。在接下來的幾個月的時間裏,咱們打算對如下話題(或者還有其餘未列出的話題)進行討論,來作一個IL2CPP深刻講解系列。目前準備討論的話題有:ide
1.基礎 - 工具鏈和命令行參數(本篇博文)工具
2.IL2CPP生成代碼介紹測試
3.IL2CPP生成代碼調試小竅門webgl
4.方法調用介紹(通常方法調用和虛方法調用等)ui
5. 通用代碼共享的實現spa
6.P/invoke(Platform Invocation Service)對於類型(types)和方法(methods)的封裝
7.垃圾回收器的集成
8.測試框架(Testing frameworks)及其使用
爲了能讓這個系列的討論成爲可能,咱們會涉及到一些未來確定會進行改動的IL2CPP的實現細節。但這也沒有關係,經過這些討論,咱們但願能給你們提供一些有用和有趣的信息。
從技術層面上來講,咱們說的IL2CPP包含了兩部分:一個進行 預先編譯(譯註:ahead-of-time,又叫AOT,如下一概使用AOT縮寫)的編譯器。
AOT編譯器將由.NET 輸出的中間語言(IL)代碼生成爲C++代碼。運行時庫則提供諸如垃圾回收,與平臺無關的線程,IO以及內部調用(C++原生代碼直接訪問託管代碼結構)這樣的服務和抽象層。
IL2CPP AOT編譯器實際的執行文件是il2cpp.exe。在Windows平臺你能夠在Unity安裝路徑的Editor\Data\il2cpp目錄下找到。對於OSX平臺,它位於Unity安裝路徑的Contents/Frameworks/il2cpp/build目錄內。 il2cpp.exe這個工具是一個託管代碼可執行文件,其徹底由C#寫成。在開發IL2CPP的過程當中,咱們同時使用.NET和Mono編譯器對其進行編譯。
il2cpp 接受來自Unity自帶的或者由Mono編譯器產生的託管程序集,將這些程序集轉換成C++代碼。這些轉換出的C++代碼最終由部署目標平臺上的C++編譯器進行編譯。
你能夠參照下圖理解IL2CPP工具鏈的做用:
IL2CPP的另一個部分就是對虛擬機提供支持的運行時庫。咱們基本上是用C++代碼來實現整個運行時庫的(好吧,其實裏面仍是有一些和平臺相關的代碼使用了程序集,這個只要你知我知便好,不要告訴別人 )。咱們把運行時庫稱之爲libli2cpp,它是做爲一個靜態庫被鏈接到最終的遊戲可執行文件中。這麼作的一個主要的好處是可使得整個IL2CPP技術是簡單而且是可移植的。
你能經過查看隨Unity一塊兒發佈的libil2cpp頭文件來窺探其代碼組織方式(Windows平臺,頭文件在Editor\Data\PlaybackEngines\webglsupport\BuildTools\Libraries\libil2cpp\include目錄中。OSX平臺,頭文件在Contents/Frameworks/il2cpp/libil2cpp目錄中)。舉個例子,由il2cpp產生的C++代碼和libil2cpp之間的接口API,存在於codegen/il2cpp-codegen.h這個文件中。
運行時的另一個重要的部分,就是垃圾收集器。在Unity 5中,咱們使用libgc垃圾收集器。它是一個典型的貝姆垃圾收集器(Boehm-Demers-Weiser garbage collector)。(譯註:相對使用保守垃圾回收策略)。然而咱們的libil2cpp被設計成能夠方便使用其餘垃圾回收器。所以咱們如今也在研究集成微軟開源的垃圾回收器(Microsoft GC)。對於垃圾回收器這一點,咱們會在後續的一篇中專門的討論,這裏就很少說了。
讓咱們從一個簡單的例子入手。這裏使用Unity的版本是5.0.1,在Windows環境而且創建一個全新的空項目。而後建立一個帶MonoBehaviour的腳本文件,將其做爲組件加入到Main Camera上。代碼也是很是的簡單,輸出Hello World:
using UnityEngine;
public class HelloWorld : MonoBehaviour {
void Start () {
Debug.Log("Hello, IL2CPP!");
}
}
當我切換到WebGL平臺進行項目生成的時候,咱們能夠用Process Explorer來對il2cpp的命令行進行觀察,獲得如下內容:
"C:\Program Files\Unity\Editor\Data\MonoBleedingEdge\bin\mono.exe"
"C:\Program Files\Unity\Editor\Data\il2cpp/il2cpp.exe" --copy-level=None --enable-generic-sharing --enable-unity-event-support --output-format=Compact --extra-types.file="C:\Program Files\Unity\Editor\Data\il2cpp\il2cpp_default_extra_types.txt"
"C:\Users\Josh Peterson\Documents\IL2CPP Blog Example\Temp\StagingArea\Data\Managed\Assembly-CSharp.dll"
"C:\Users\Josh Peterson\Documents\IL2CPP Blog Example\Temp\StagingArea\Data\Managed\UnityEngine.UI.dll"
"C:\Users\Josh Peterson\Documents\IL2CPP Blog Example\Temp\StagingArea\Data\il2cppOutput"
嗯,這個真是老太太的裹腳布 - 又臭又長......,因此讓咱們把命令分拆一下,Unity運行的是這個可執行文件:
"C:\Program Files\Unity\Editor\Data\MonoBleedingEdge\bin\mono.exe"
下一個參數是il2cpp.exe工具自己:
"C:\Program Files\Unity\Editor\Data\il2cpp/il2cpp.exe"
請注意剩下的參數其實都是傳遞給il2cpp.exe的而不是mono.exe。上面的例子裏傳遞了5個參數給il2cpp.exe:
–copy-level=None
指明il2cpp.exe不對生成的C++文件進行copy操做
告訴IL2CPP若是能夠,對通用方法進行共享。這個能夠減小代碼並下降最後二進制文件的尺寸,確保和Unity events相關的,經過反射機制來運做的代碼,可以正確生成。
在生成C++代碼時爲裏面的類型和方法使用更短的名字。這會使得C++代碼難以閱讀,由於原來在IL中的名字被更短的取代了。但好處是可讓C++編譯器運行的更快。
–extra-types.file=」C:\Program Files\Unity\Editor\Data\il2cpp\il2cpp_default_extra_types.txt」
使用默認的(也是空的)額外類型文件。
il2cpp.exe會將在這個文件中出現的基本類型或者數組類型看做是在運行時生成的而不是一開始出如今IL代碼中來對待。
須要注意的是這些參數可能會在之後的Unity版本中有所變化。咱們如今尚未穩定到把il2cpp.exe的命令行參數整理固定下來的階段。
最後,咱們有由兩個文件組成的一個列表和一個目錄在這個長長的命令行中:
「C:\Users\Josh Peterson\Documents\IL2CPP Blog Example\Temp\StagingArea\Data\Managed\Assembly-CSharp.dll」
「C:\Users\Josh Peterson\Documents\IL2CPP Blog Example\Temp\StagingArea\Data\Managed\UnityEngine.UI.dll」
「C:\Users\Josh Peterson\Documents\IL2CPP Blog Example\Temp\StagingArea\Data\il2cppOutput」
il2cpp.exe工具能夠接收一個由IL程序集組成的列表。在上面這個例子中,程序集包含了項目中的簡單腳本程序集:Assembly-CSharp.dll,和GUI程序集:UnityEngine.UI.dll。你們可能會注意到這裏面明顯少了什麼:UnityEngine.dll到哪去了?系統底層的mscorlib.dll也不見了蹤跡。實際上,il2cpp.exe會在內部自動引用這些程序集。你固然也能夠把這些放入列表中,但他們不是必須的。你只須要說起那些根程序集(那些沒有被其餘任何程序集引用到的程序集),剩下的il2cpp.exe會根據引用關係自動加入。
裹腳布的最後一塊是一個目錄,il2cpp.exe會將最終的C++代碼生成到這裏。若是你還保持着一顆好奇的心,能夠看看這個目錄中產生的文件。這些文件是咱們下一個討論的主題。在你審視這些代碼前,能夠考慮將WebGL構建設置中的「Development Player」選項勾上。這麼作會移除–output-format=Compact命令行參數從而讓C++代碼中的類型和方法的名字更加可讀。
嘗試在WebGL或者iOS構建設置中進行些改變。這樣你會發現傳遞給il2cpp.exe的參數也會相應的發生變化。例如,將「Enable Exceptions」 設置成「Full」 會將–emit-null-checks,–enable-stacktrace,和 –enable-array-bounds-check這三個參數加入il2cpp.exe命令行。
我想指出IL2CPP有一貫挑戰咱們沒有接受,並且咱們也高興咱們忽略了它。咱們沒有嘗試重寫整個C#標準庫。當你使用IL2CPP後端構建Unity項目的時候,全部在mscorlib.dll,System.dll等中的C#標準庫和原來使用Mono編譯時候的如出一轍。
咱們能夠依賴健壯的且久經考驗的C#標準庫,因此當處理有關IL2CPP的bug的時候,咱們能夠很確定的說問題出在AOT編譯器或者運行時庫這兩個地方而不是在其餘地方。
自從咱們在一月份的4.6.1 p5版本中首次引入IL2CPP以來,咱們已經連續發佈了6個Unity版本和7個補丁(Unity版本號跨越4.6和5.0)。在這些發佈中咱們修正了超過100個bug。
爲了確保持續的改進得以實施,咱們內部只保留一份最新的開發代碼在主幹分之(trunk branch)上,在發佈各個版本以前,咱們會將IL2CPP的改動掛到一個特定的分之下,而後進行測試,確保全部的bug已經正確的修正了。咱們的QA和維護工做組爲此付出了驚人的努力才得以保證發佈版本的快速迭代。(譯註:感受是版本管理的標準的開發流程,另外由文中提到的trunk branch來看,他們貌似還在使用SVN)
提供高質量Bug的用戶社區被證實是一個無價之寶。咱們很是感謝用戶的反饋來幫助咱們改進IL2CPP,而且但願這類反饋越多越好。
咱們的IL2CPP研發組有很強烈的「測試優先」意識。咱們時常使用「Test Driven Design」方法,在沒有進行足夠全面的測試的狀況下,幾乎不會進行代碼的合併工做。這個策略用在IL2CPP項目上很是的棒。咱們如今所面對的大部分bug並非意想不到的行爲產生的,而是由意想不到的特殊狀況產生的。(例如在一個32位的索引數組中使用了64位的指針從而致使C++編譯器失敗)面對這種類型的bug咱們能夠快速的而且很自信的進行修正。
有了社區的幫助,咱們很是努力的讓IL2CPP既快又穩定。順便說一句,若是你對我剛纔說的這些有興趣,咱們正在招人(嗯.....我只是這麼一說)
關於IL2CPP咱們還有不少能夠說的。下一次咱們會深刻到il2cpp.exe代碼生成的細節中。看看對於C++編譯器來講,由il2cpp.exe生成的代碼會是個什麼樣子。