Dalvik 移植指導
Dalvik虛擬機可以在很多平台上運行,這些平台的操作系統至少是一個運行著GNUC編譯器的類似於UNIX的平台(Linux,BSD,Mac OS X)。本文檔指導讀者如何把Dalvik虛擬機移植到一個Linux平台上,本文檔假定我們要移植的平台和目前Android平台在代碼架構上有一定的相似性,可以進行移植。
核心庫的移植
核心庫的源代碼主要在Dalvik/libcore和dalvik/vm/native這兩個文件夾中。核心庫的源代碼是用C語言和C++寫成的,因此在Linux環境下不需要更改。核心庫的代碼很多都是來自Apache Harmony項目,但是也有一些是來自OpenSSL、zlb和ICU等項目,因此,為了虛擬機的運行,這些項目需要被移植到新平台上。
JNI Call Bridge 的移植
DVM的運行庫絕大部分都是用portable C編寫的,其中的一個例外是JNI call bridge。簡單來說,它的作用是把一系列的整型值轉變成各種類型的函數參數,並且調用函數。這個調用過程必須符合C函數調用的約定。
為了簡化移植,JNI Call Bridge在新平台上通常會使用開源的FFI庫(我覺得,這種庫,大概類似於java一樣,具有通用性吧)。但是,FFI運行不夠快,也沒有對平台做專門優化,所以,移植JNI Call Bridge首先應該重新寫一個FFI庫。
JNI Call Bridge代碼在dalvik/vm/arch/*這個位置上,同時,基於FFI的版本(我覺得大概是在移植的時候的通用版本)在“generic”目錄下。每一種架構都有兩個源文件,其中一個是定義了JNI Call Bridge函數,函數如下:
void dvmPlatformInvoke(void* pEnv, ClassObject* clazz, int argInfo, int argc, const u4* argv, const char* signature, void* func, JValue* pReturn)
這個函數會調用如下C/C++函數:(我想應該是JNI函數)
return_type func(JNIEnv* pEnv, Object* this [, args])
或者
return_type func(JNIEnv* pEnv, ClassObject* clazz [, args]) (適用於靜態方法)
dvmPlatformInvoke是把argv所指向的值轉化為符合C類型調用的值,而後調用我上面指出的函數,再把得到的返回值放入JValue* pReturn所指向的地方。該函數可能使用方法簽名來決定如何處理函數中的相關值。至於方法簽名,它是一個短小的DEX簽名,用一個字符對應一個返回值和一個參數。
而另外一個源文件(前面提過有兩個源文件)則定義了一個32位的“hint”。當相應的方法類型被加載時,hint值就會被計算出來,作為“arginfo”參數進入dvmPlatformInvoke函數,hint可以用來使dvmPlatformInvoke停止例如掃面函數的返回值、總體參數的大小,以及整形參數64字節條件測試的限制等的ASCII方法簽名。(我想hint應該是決定函數是否對返回值或者參數進行方法簽名掃描)
解釋器的移植
Dalvik虛擬機運行庫含有兩個解釋器,分別是標以“移動型”和“快速型”。
移動型主大體上就是一個C函數,在任何裝有GCC的系統上都應該可以被編譯出來。(如果你的機器沒裝有GCC,那麼你應該停用“threaded”模塊,因為這個模塊依賴於GCC的goto語句的目錄來執行的。不清楚的話可以查找THREADED_INTERP的定義。
快速型使用手工匯編導致的碎片(??這裡不懂)。如果目前系統裡沒有解釋器可用,那麼系統就會從C stubs中生成一個解釋器,這個解釋器運行速度比移動型慢了很多,說它是快速解釋器,實在是名不符實。
快速型在系統裡是被默認使能的,如果源代碼不支持快速型。那麼該如何默認使能移動型呢?這可以通過dalvik.vm.execution-mode system來實現。例如。你打入如下一行:
adb shell "echo dalvik.vm.execution-mode = int:portable >> /data/local.prop"
然後重啟,這樣android應用層框架啟動時就自動會使能移動型解釋器了。
Mterp 解釋器架構
如果用匯編語言重寫解釋器的話,解釋器的效能應該會有一個明顯的提高再加上相應平台專用架構的優化,dalvik可以用一個指令一次執行完畢(??這裡也不懂)
實現解釋器最簡單的方法究竟是用一個大型的開關語句。每條指令執行完畢後,解釋器就跳到循環的頂部,根據條件跳到合適的位置(我想是類似於goto語句,執行完畢後回到頂部)。
使用線程化執行可以對這種方法進行改進。在每條指令執行的末端就包含下一條指令的取值和分派(我想是類似於流水線架構吧)。這樣會使解釋器變得有些龐大,但是就不用再跳回到開關語句的頂部了,而後者代價不菲。
而Mterp解釋器更加先進一些,用一個計算過的“goto”,來代替goto語句的(跳轉)表,避免從跳轉表中查找符合的地址,後者每次執行都需要再從內存中取的指令。Mterp的操作碼都是固定值,Mterp中的每個處理程序都有64個字節的空間,當然這無法滿足所有程序的需要,那些無法滿足的,可以通過子程或者干脆在基本空間(64個字節)之外再添加代碼。Dalvik會自動處理其中的一些程序,但是,直到VM執行的時候dalvik才會檢測是否有程序溢出(超過64個字節)。對於每條處理程序64個字節空間的決定看起來多少有些任意選擇的意味,但是卻證明對於ARM和X86架構都很不錯。
在開發的進程中每個處理程序最好同時混有C語言好和匯編語言,而且可以在解決難問題的時候隨意在這兩種語言中切換進行。Mterp就是這樣的,在如果你看一下dalvik/vm/mterp/out 目錄,你就會發現C編譯器和匯編編譯器總是在同時執行。
解釋器的代碼在dalvik/vm/mterp文件夾裡,如果你沒有,那麼現在你應該好好讀讀alvik/vm/mterp/README.txt這個文檔了。