移動開發
深入了解Android:Java虛拟機ART
鄧凡平 著

第1章
本書必讀
1.1 概述
筆者寫書向來是最後才寫第一章。此時,全書的主體内容已完全确定,筆者在學習ART虛拟機以及編撰本書的過程中所遇到的問題、總結的經驗和教訓等才可以完整地彙總并分享給各位讀者。是以,本章是全書的點睛之筆,為必讀章節。并且,我相信随着讀者閱讀的深入,還會時常回顧本章。
總體來說,本書并不簡單。其實,從本書的目标—Java虛拟機也可以想得到,對Java應用程式來說,虛拟機就算是作業系統了。哪一本講作業系統的書會簡單呢?
具體到Android ART虛拟機來說,本書以Android 7.0為參考,絕大部分待分析的源代碼位于art目錄中。
□包含C++代碼1071個檔案。其中,.cc檔案中包含236 744有效代碼行(即不算注釋及空行),.h檔案中包含74 710有效代碼行。
□包含彙編檔案1704個檔案,覆寫x86、arm、mips的32位和64位6個CPU平台,有效代碼共19 955行。
也就是說,我們的ART虛拟機是一個有着30多萬行代碼的龐然大物。針對這樣一個複雜的系統,要想從一個對它略知一二的初學者成長為一個能品頭論足甚至指點江山的熟練者,這一路的學習曆程必然不會輕松。
接下來,筆者将介紹閱讀本書時必須準備的工具。磨刀不誤砍柴工,建議讀者先把這些工具準備好之後再開始後面的學習。
1.2 準備環境和工具
為了更好學習ART,讀者要準備好如下的環境或工具。
1.2.1 準備源代碼
首先,我們需要一份Android 7.0的源代碼。筆者在百度雲盤上提供了本書所需的資料下載下傳。讀者也可以到清華大學開源軟體鏡像站按照網頁裡的說明下載下傳。其官網位址為
https://mirrors.tuna.tsinghua.edu.cn/help/AOSP/。筆者總結其下載下傳步驟如下。
源代碼的量很大,讀者需要有一個浏覽它的工具,Source Insight是不二之選。下面我們來看看如何配置它。
1.2.2 準備Source Insight
Source Insight是閱讀源碼的必備工具,它是一個Windows軟體,在Linux平台上可通過wine進行安裝。
提示:Source Insight推出3.5版本之後,很長一段時間都沒有更新。最近推出了全新的4.0版本。但經過筆者測試,4.0版本的Source Insight在Linux上表現不穩定,建議讀者在Linux上使用3.5版本的Source Insight。下面的講解也以3.5版本的Source Insight為主。
首先,打開Source Insight,通過菜單項Project→New Project建立一個源碼工程。工程可建立在Android 7.0源碼根目錄。筆者存放的位置是~/workspace/aosp/android-7.0,工程名為android-7.0。
接下來我們要先設定源碼檔案的字尾名。在ART中,C++的實作檔案以.cc為檔案字尾名。而彙編源碼存儲在以.S為字尾的檔案裡。Source Insight預設的配置不識别.cc和.S為字尾的源碼檔案,是以我們需要修改它。
單擊菜單項Options→Document Options,彈出圖1-1所示的檔案類型對話框。
圖1-1用于為C++源碼添加.cc結尾的檔案類型。接着還要為彙編源碼做類似的處理,來看圖1-2。
接下來我們為android-7.0工程添加具體的源碼檔案。單擊菜單項Project→Add and Remove Project Files,彈出工程檔案選擇對話框,如圖1-3所示。
請讀者添加如下目錄到android-7.0工程中。
□art目錄(通過圖中的Add Tree按鈕可添加整個目錄):ART虛拟機源碼檔案。
□libcore目錄:包含JDK相關源碼檔案。
□libnativehelper目錄:包含JNI相關源碼,如jni.h等。
□frameworks/base/cmds/am、frameworks/base/core、frameworks/base/include三個目錄:包含Zygote相關源碼檔案。
另外,上述目錄中還有很多用于測試的源碼檔案,數量非常多。由于它們對本書的學習并無影響,建議讀者移除其中test目錄下的源碼檔案—通過圖1-2中的Remove Tree可移除指定目錄中的源碼。比如art/test包含的1800多個源碼檔案都可以移除。
接着要進一步配置Source Insight。ART是一個複雜系統,是以谷歌用了一些工具來輔助編寫正确的源碼。這些工具要求在源碼函數聲明、變量定義等地方使用一些特殊的宏,而Source Insight不認識這些宏,是以很多函數、變量都無法解析和識别。為此,我們需要配置Source Insight,讓它忽略這些宏。配置方法下面将詳細介紹。
首先,找到Source Insight的C.tom檔案,它位于~/.wine/drive_c/Program Files (x86)/Source Insight 3/下。打開該檔案,在檔案末尾添加如下的内容。
[C.tom檔案]
;C.tom是C Token Macros的意思,用于重定義C/C++檔案中的宏
;下面的條目都是ART源碼中出現的宏,我們将它們定義為空,這樣,Source Insight碰到這些宏
;時就會忽略它們
SHARED_TRYLOCK_FUNCTION(...)
ACQUIRE_SHARED()
EXCLUSIVE_TRYLOCK_FUNCTION(...)
SCOPED_CAPABILITY
SHARED_REQUIRES(...)
REQUIRES(...)
UNLOCK_FUNCTION(...)
ASSERT_SHARED_CAPABILITY(...)
ASSERT_CAPABILITY(...)
__noreturn
__mallocfunc
EXCLUSIVE_LOCKS_REQUIRED(...)
LOCKS_EXCLUDED(...)
SHARED_LOCKS_REQUIRED(...)
SHARED_LOCK_FUNCTION(...)
DEFAULT_MUTEX_ACQUIRED_AFTER
ACQUIRE(...)
ACQUIRE()
RELEASE()
RELEASE_SHARED()
ACQUIRED_AFTER(...)
GUARDED_BY(...)
PACKED(...)
__nonnull(...)
OVERRIDE
SHARED_LOCKABLE
ATTRIBUTE_UNUSED
NO_THREAD_SAFETY_ANALYSIS
ALWAYS_INLINE
配置好C.tom後,關閉并重新打開Source Insight,單擊Project→Rebuild Project,彈出圖1-4所示的對話框。
圖1-4中,選擇Re-Create the whole project from scratch即可。
提示:圖1-4所示對話框的下方展示了源碼檔案個數,筆者設定的工程包含源碼檔案8688個。
1.2.3 準備模拟器和自制系統鏡像
閱讀源碼是學習虛拟機的主要方法。但在某些關鍵地方,有時候很難确定代碼邏輯的走向,這時就需要在源碼中加一些日志來輔助我們觀察虛拟機的行為。在此,筆者推薦使用模拟器和自制系統鏡像來幫助我們達到這個目标。
提示:自制系統鏡像是由上文下載下傳的Android源碼檔案編譯而來。我們可以随心所欲地通過修改源碼檔案來定制Android系統。當然,這個由我們自己編譯而來的系統隻能跑在模拟器中。即便如此,這對我們學習ART虛拟機來說也是莫大的幫助。
1.2.3.1 準備好模拟器
讀者需首先安裝Android Studio。然後随便打開一個Android應用工程。單擊菜單欄右邊的avd manager圖示,啟動AVD界面,如圖1-5所示。
圖1-5中,筆者建立了兩個虛拟裝置,一個是運作Android 7.0系統的innost-7.0裝置,一個是運作Android 9.0系統的innost-9.0裝置。
單擊圖1-6中左下角的“Create Virtual Device”,出現圖1-6所示的裝置硬體配置界面。
讀者可自定義硬體配置或者從谷歌相關手機産品中選一個手機型号。比如Pixel XL。然後單擊圖1-6右下角的Next。出現圖1-7所示的系統鏡像選擇界面。
建議讀者選擇Nougat x86系統鏡像。也就是說,我們後面要分析的ART虛拟機将以x86 CPU為平台。
為什麼選擇x86平台?
工作用的桌上型電腦或筆記本主要是x86平台。是以,模拟器運作x86系統鏡像的速度非常快。筆者之前嘗試過使用arm平台,但模拟器運作的速度較慢。另外,根據上一節筆者統計的代碼量可知,6個CPU平台總彙編代碼的有效代碼行數/總有效代碼行數大概為6.02%,平均每個CPU平台的彙編代碼行數才占總代碼行數的1%左右。從這一點可以看出,彙編代碼雖然重要,但它不會影響虛拟機學習。值得注意的是,Android SDK從8.0開始就不再提供ARM平台的模拟器鏡像檔案。
虛拟裝置準備就緒後,讀者可以啟動它。這時,這個虛拟裝置運作的是官方提供的鏡像。
1.2.3.2 自制系統鏡像
現在,我們有了Android源碼、虛拟裝置和官方下載下傳的鏡像檔案。接下來需要編譯Android源碼以生成一個系統鏡像檔案,然後用這個系統鏡像檔案來啟動虛拟裝置。如此,就達到了讓虛拟裝置運作我們定制的系統鏡像的目标。
編譯系統的步驟如下。
執行lunch指令後,會顯示如圖1-8所示的内容,裡邊是各種不同的目标裝置。請讀者選擇第8項(下面将介紹第8項的來曆)。它表示要編譯裝置類型為"innost"的裝置,該裝置使用的CPU為x86,編譯類型為userdebug。接着看下一步。
最後,讓模拟器使用我們編譯得到的系統鏡像檔案,方法如下。
由于本書的目标是研究ART虛拟機,是以,我們自己編譯的系統鏡像并不需要包含太多的應用程式,隻要保證系統啟動必需的幾個核心應用程式即可。為此,筆者在源碼根目錄/device下新增了一個名為innost的裝置類型。圖1-9展示了該目錄下的檔案。
圖1-9展示了innost裝置類型下包含的檔案。當把這些準備好後,我們執行如下指令時才能出現圖1-8中的第7和第8項。
提示:讀者可從筆者分享的連結中下載下傳如圖1-9所示的innost裝置目錄檔案。本書所有資源的下載下傳說明見1.4節的内容。
如果讀者下載下傳了筆者分享的Android 7.0源碼的話,device目錄下已經包含了innost裝置目錄的檔案。
1.2.4 小結
讀者閱讀到這個地方時,請檢查下面的工作是否完成。
□有一份完整的Android 7.0的源碼。讀者可以從筆者提供的資源連結中下載下傳,或者從清華大學開源鏡像站下載下傳(下載下傳步驟見1.2.1節)。
□配置好Source Insight,包括添加.cc和.S為字尾的檔案類型、修改C.tom檔案。然後,導入ART虛拟機學習所需的源碼目錄(art、libnativehelper、libcore、frameworks/base/cmds/am、frameworks/base/core、frameworks/base/include,可以把test相關的源碼去除)。
□下載下傳Nougat x86系統鏡像,建立好對應的模拟器,并啟動它。
□編譯7.0的源碼。如果讀者是自行下載下傳的源碼,請從筆者提供的資料連結中下載下傳自制系統鏡像所需的裝置配置檔案(存放在源碼根目錄/device下)。
□通過emulator指令使用自己編譯出來的系統鏡像檔案啟動模拟器。
1.3 本書的内容
本書大體上可以分為五個部分,筆者用表1-1來描述各個部分對應章節的内容和說明。請讀者務必認真閱讀(後續如果需要,也請經常回顧)。
提示:表1-1最後一列是筆者給各章節難度的一個主觀評分。評分的目的在于提醒讀者閱讀各章時可能會感受到的難度。除了第6章有着超高難度之外,其他章節隻要肯花時間,相信對大部分讀者總能學會。另外,筆者自己在研究ART源碼的時候會碰到這樣一種情況,有些代碼前幾次閱讀感覺難度比較大,但隻要多讀幾次,總會有茅塞頓開的時候。或許這就是所謂的量變到質變吧。
筆者再次和讀者強調兩點:
□ART虛拟機是複雜系統,子產品之間有非常強的耦合關系。讀者需采用剝洋蔥式的學習方法,逐漸、多角度來學習它。比如,Heap子產品本書有三處地方介紹了它。每一次介紹都隻關注Heap子產品一部分的知識。初學者切莫盯着一個知識點一頭紮入,否則很難走下去。
□如果讀者不是特别了解ART的話,建議嚴格按照本書的順序來閱讀相關章節。
1.4 本書資源下載下傳說明
讀者可通過筆者的部落格blog.csdn.net/innost首頁置頂文章“深入了解Android系列書籍資源分享更新”以檢視本書的資源下載下傳位址。目前本書提供的下載下傳資料如表1-2所示。
如果說ART虛拟機是一座堅固的城堡的話,本書相當于在這個城堡上為讀者們打開了好幾個關鍵突破口。希望讀者在此基礎上繼續研究ART虛拟機中其他有意思、有價值的領域。