虛擬機(jī)其實(shí)我們并不陌生报强,比如vmware,virtualbox等PC平臺上的虛擬機(jī)軟件瞒御,相信很多人都玩過钾腺。比如在windows上跑一個ubuntu來研究一下linux,編譯一下android源碼什么的饺汹。使用虛擬機(jī)一般有兩個目的蛔添,一個是平臺模擬,一個是便于維護(hù)首繁。
我們知道系統(tǒng)的底層是基于CPU設(shè)計(jì)的硬件平臺作郭,而CPU根據(jù)應(yīng)用場景不同有著不同的指令集和寄存器結(jié)構(gòu),也就是架構(gòu)弦疮。常見的PC平臺有x86夹攒、x86_64架構(gòu),移動平臺有arm架構(gòu)。這些架構(gòu)的差異使得不同的系統(tǒng)無法直接跨平臺運(yùn)行胁塞,因?yàn)橄到y(tǒng)源碼最終要被編譯成機(jī)器指令才能運(yùn)行咏尝。當(dāng)然有些系統(tǒng)支持了非常豐富的硬件平臺压语,比如Linux,既可以在x86平臺運(yùn)行编检,也可以在arm平臺上運(yùn)行胎食。但是如果我們要在x86平臺上運(yùn)行最初只支持arm結(jié)構(gòu)的Android系統(tǒng),我們就需要一個中間層允懂,模擬出一個arm平臺厕怜,將arm架構(gòu)的指令和寄存器操作,使用軟件功能來實(shí)現(xiàn)蕾总。有的虛擬機(jī)為了提高執(zhí)行效率粥航,也有將目標(biāo)平臺的指令,直接翻譯成宿主平臺指令執(zhí)行的生百,具體一點(diǎn)可以參考這里递雀,虛擬機(jī)的原理大致如下圖:
我們說的平臺模擬的虛擬機(jī)就屬于TYPE2類型,底層的OS就是宿主系統(tǒng)蚀浆,上層的OS就是目標(biāo)系統(tǒng)缀程,中間的HYPER VISOR就是虛擬機(jī)軟件。Android虛擬機(jī)也是平臺虛擬機(jī)的一種市俊,為了后續(xù)方便研究杨凑,我們先總結(jié)一下PC上虛擬機(jī)的運(yùn)行過程。首先我們需要下載一個虛擬機(jī)軟件秕衙,然后在軟件里創(chuàng)建虛擬機(jī)蠢甲,指定虛擬的硬件配置僵刮,虛擬機(jī)數(shù)據(jù)會存儲在一個虛擬磁盤文件里据忘,最后我們需要為虛擬機(jī)安裝目標(biāo)系統(tǒng),然后就可以啟動虛擬機(jī)了搞糕。所以它包含幾個主要部分:
- 虛擬機(jī)軟件
- 硬件配置
- 虛擬機(jī)文件
- 目標(biāo)系統(tǒng)
虛擬機(jī)和真機(jī)差異
我們運(yùn)行自己APP勇吊,既可選擇真機(jī)調(diào)試運(yùn)行,也可以使用虛擬機(jī)窍仰。一般情況下汉规,使用真機(jī)速度更快,體驗(yàn)更佳驹吮,但是真機(jī)也有自己的一些缺陷针史。比如,獲取成本高碟狞,假如要測試APP的兼容性啄枕,針對不同API版本的機(jī)型都進(jìn)行配置的話,將會是一筆不少的花費(fèi)族沃。其次频祝,真機(jī)的系統(tǒng)是經(jīng)過OEM廠家定制的泌参,不一定是原生的API和內(nèi)在邏輯,也無法針對GoogleAPI等服務(wù)進(jìn)行開發(fā)常空,如果機(jī)器里沒有的話沽一。特別地,當(dāng)你想要燒錄自己編譯的源碼漓糙,對FrameWork或者更加底層的邏輯行調(diào)試的時候铣缠,真機(jī)則更加不方便。OEM廠家一般都會鎖定刷機(jī)功能昆禽,比如FastBoot攘残,只能用廠家自己的刷機(jī)工具進(jìn)行刷機(jī),一般也不容獲取为狸。并且每家的源碼和鏡像文件格式都不相同歼郭,編譯的標(biāo)準(zhǔn)源碼無法直接燒錄。所以辐棒,如果要進(jìn)行Android系統(tǒng)學(xué)習(xí)病曾,學(xué)習(xí)虛擬機(jī)的使用還是非常必要的。
使用方式及差異
Android虛擬機(jī)的獲取方式有兩種漾根,一種是下載AndroidStudio泰涂,使用AndroidStudio進(jìn)行虛擬機(jī)的創(chuàng)建,下載對應(yīng)的系統(tǒng)鏡像辐怕,虛擬機(jī)運(yùn)行之后就可以當(dāng)做設(shè)備一樣使用adb連接調(diào)試了逼蒙。還有一種是下載Android sdk tool,使用avd manager命令進(jìn)行安裝、運(yùn)行寄疏。第一種的優(yōu)勢是方便是牢、直觀,根據(jù)UI提示一步一步操作就可以創(chuàng)建陕截、運(yùn)行虛擬機(jī)驳棱,實(shí)際上它也是使用avd manager的功能。第二種的優(yōu)勢是可以了解adv manager的工作原理农曲,可以使用參數(shù)進(jìn)行最靈活的配置社搅,可以定制自己的kernel、system鏡像等等乳规。我們要研究的就是第二種形葬。
從官方的教程來看,Android虛擬機(jī)是基于QEMU開源虛擬機(jī)衍生出來的暮的,如果想深入研究一下虛擬機(jī)原理笙以,可以研究一下QEMU。如果要看官方教程請戳這里青扔,QEMU官方教程請戳這里源织。
虛擬機(jī)的安裝
從官方鏈接上翩伪,我們可以下載到不同平臺的AndroidStudio,其中自帶了整套的AndroidSdk工具谈息。如果我們不需要AndroidStudio缘屹,比如在構(gòu)建服務(wù)器上,我們也可以只下載sdk工具侠仇,通過工具在線下載build tool轻姿、不同版本的sdk、創(chuàng)建虛擬機(jī)等等逻炊。這里不做詳細(xì)的介紹互亮。
安裝完之后,將SDK路徑/tools添加到PATH環(huán)境變量中余素,便于使用豹休。也可以直接到sdk路徑下面運(yùn)行命令,MAC平臺的SDK路徑是:
/Users/your_name/Library/Android/sdk/
虛擬機(jī)的運(yùn)行
使用avdmanager命令行創(chuàng)建虛擬機(jī)比較復(fù)雜桨吊,也是理解虛擬機(jī)的核心部分威根,我們稍后一步一步探索。
假如我們有一個創(chuàng)建好的虛擬機(jī)视乐,比如我們可以先使用AndroidStudio創(chuàng)建一個洛搀,就可以使用以下的命令直接運(yùn)行虛擬機(jī)。
emulator -avd avd_name [ {-option [value]} … ]
或者
emulator @avd_name [ {-option [value]} … ]
可以看到這個emulator就是我們虛擬機(jī)程序正主了佑淀,我們到sdk安裝目錄下去看看它是個什么文件留美。
heavy:~/Library/Android/sdk/tools$ file emulator
emulator: Mach-O 64-bit executable x86_64
heavy:~/Library/Android/sdk/tools$ ls -alh emulator
-rwxr-xr-x 1 heavy staff 253K 1 2 2018 emulator
可以看到,emulator是一個可執(zhí)行文件伸刃,但是體積很小谎砾,按照虛擬機(jī)的功能和復(fù)雜性而言,應(yīng)該是不只這個大小的奕枝,所以合理推測它應(yīng)該還依賴了其他的可執(zhí)行文件或者庫文件棺榔,這里先不研究瓶堕,就把它當(dāng)做虛擬機(jī)程序隘道。
虛擬機(jī)的存儲路徑
根據(jù)PC虛擬機(jī)使用總結(jié),虛擬機(jī)要運(yùn)行還需要安裝了目標(biāo)系統(tǒng)的虛擬磁盤文件郎笆,這里通過-avd參數(shù)的名稱指定了磁盤文件位置谭梗,我們來看看虛擬機(jī)是怎么根據(jù)名稱來找到這個文件的。
我們執(zhí)行以下命令:
./bin/avdmanager list avd
可以看到輸出:
Parsing /Users/heavy/Library/Android/sdk/build-tools/21.1.1/package.xml
...
...
Parsing /Users/heavy/Library/Android/sdk/system-images/android-23-bak/google_apis/x86_64/package.xml
...
Name: Nexus_5X_API_23
Device: Nexus 5X (Google)
Path: /Users/heavy/.android/avd/Nexus_5X_API_23.avd
Target: Google APIs (Google Inc.)
Based on: Android 6.0 (Marshmallow) Tag/ABI: google_apis/x86_64
Skin: nexus_5x
Sdcard: 512M
avdmanager解析了很多的平臺配置文件:package.xml宛蚓,最后告訴我們找到一個可用的虛擬機(jī)激捏,位置在主目錄下面。我們打開文Nexus_5X_API_23.avd件查看內(nèi)容:
avd.ini.encoding=UTF-8
path=/Users/heavy/.android/avd/Nexus_5X_API_23.avd
path.rel=avd/Nexus_5X_API_23.avd
target=android-23
發(fā)現(xiàn)它只是一個配置文件凄吏,通過path和path.rel指向虛擬機(jī)數(shù)據(jù)文件夾的絕對远舅、相對地址闰蛔。我們再查看目錄的內(nèi)容:
drwxr-xr-x 18 heavy staff 576B 12 4 10:24 .
drwxr-xr-x 4 heavy staff 128B 12 4 10:34 ..
-rw-r--r-- 1 heavy staff 66M 12 3 14:25 cache.img
-rw-r--r-- 1 heavy staff 4.3M 12 4 10:24 cache.img.qcow2
-rw-r--r-- 1 heavy staff 1.1K 12 3 14:25 config.ini
drwxr-xr-x 7 heavy staff 224B 12 3 14:25 data
-rw-r--r-- 1 heavy staff 51B 12 4 10:24 emulator-user.ini
-rw-r--r-- 1 heavy staff 1.0M 12 3 14:25 encryptionkey.img
-rw-r--r-- 1 heavy staff 448K 12 4 10:24 encryptionkey.img.qcow2
-rw-r--r-- 1 heavy staff 1.9K 12 4 10:24 hardware-qemu.ini
-rw-r--r-- 1 heavy staff 512M 12 3 14:25 sdcard.img
-rw-r--r-- 1 heavy staff 768K 12 4 10:24 sdcard.img.qcow2
drwxr-xr-x 3 heavy staff 96B 12 3 14:26 snapshots
-rw-r--r-- 1 heavy staff 192K 12 3 14:25 system.img.qcow2
-rw-r--r-- 1 heavy staff 2.0G 12 3 14:25 userdata-qemu.img
-rw-r--r-- 1 heavy staff 326M 12 4 10:24 userdata-qemu.img.qcow2
-rw-r--r-- 1 heavy staff 2.0G 12 3 14:25 userdata.img
-rw-r--r-- 1 heavy staff 8B 12 3 14:25 version_num.cache
我們就找到了虛擬機(jī)使用的相關(guān)數(shù)據(jù)了,具體的文件內(nèi)容稍后研究⊥及兀現(xiàn)在我們回頭來看看序六,avdmanager是怎么找到Nexus_5X_API_23.avd文件的,為什么查找虛擬機(jī)要解析那些package.xml文件呢蚤吹。
根據(jù)官網(wǎng)的描述例诀,虛擬機(jī)數(shù)據(jù)路徑默認(rèn)規(guī)則是:
~/.android/avd/name.avd/
其中name就是虛擬機(jī)的名稱, ~是主機(jī)上的android主目錄裁着,可以通過ANDROID_SDK_HOME來動態(tài)指定繁涂。
it displays a list of AVD names from your Android home directory. Note that you can override the default home directory by setting the ANDROID_SDK_HOME environment variable.
如果設(shè)置環(huán)境變量指向一個其他的路徑,avdmanager就會到指定的路徑下去尋找虛擬機(jī)文件,下面這個api26是手動拷貝到環(huán)境變量指定的路徑下面的,但是它會被當(dāng)做一個虛擬機(jī)二驰。
heavy:~/Library/Android/sdk/tools$ echo $ANDROID_SDK_HOME
/Users/heavy/temp
heavy:~/Library/Android/sdk/tools$ ls ~/temp/.android/avd/
Nexus_5X_API_26.ini
heavy:~/Library/Android/sdk/tools$ ./bin/avdmanager list avd
Virtual Devices:
Name: Nexus_5X_API_26
Device: Nexus 5X (Google)
Path: /Users/heavy/.android/avd/Nexus_5X_API_23.avd
Target: Google APIs (Google Inc.)
Based on: Android 6.0 (Marshmallow) Tag/ABI: google_apis/x86_64
Skin: nexus_5x
Sdcard: 512M
如果我們修改了虛擬機(jī)存儲路徑的名稱:
heavy:~/.android/avd$ mv Nexus_5X_API_23.avd/ Nexus_5X_API_23.avd-bak
heavy:~/.android/avd$ ~/Library/Android/sdk/tools/bin/avdmanager list avd
Available Android Virtual Devices:
The following Android Virtual Devices could not be loaded:
Name: Nexus_5X_API_23
Path: /Users/heavy/.android/avd/Nexus_5X_API_23.avd
Error: Failed to parse properties from /Users/heavy/.android/avd/Nexus_5X_API_23.avd/config.ini
avdmanager依然可以找到虛擬機(jī)扔罪,但是加載的時候會失敗,如果我們修改了虛擬機(jī)初始化文件的路徑,avdmanager直接就找不到虛擬機(jī)了桶雀。
heavy:~/.android/avd$ ls
heavy:~/.android/avd$ mv Nexus_5X_API_23.ini Nexus_5X_API_23.ini-bak
heavy:~/.android/avd$ ls
Nexus_5X_API_23.avd Nexus_5X_API_23.ini-bak
heavy:~/.android/avd$ ~/Library/Android/sdk/tools/bin/avdmanager list avd
Available Android Virtual Devices:
說明實(shí)際上avdmanager是根據(jù)路徑規(guī)則步势,找的同名配置文件,然后根據(jù)配置文件里的路徑背犯,再尋找虛擬機(jī)的數(shù)據(jù)路徑的坏瘩。那為什么要解析sdk路徑下面的package.xml文件呢?
這里我沒有找到足夠的資料漠魏,只能做一個推測倔矾。我們知道AndroidSDK工具分了很多平臺和api版本,除去最基本的tools,plaform-tools等工具是sdk必備的柱锹,其他的都是可以根據(jù)需要選裝的哪自,所以google提供了sdkmanager作為一個工具,用于管理本地的sdk工具集禁熏,包括從遠(yuǎn)程倉庫下載和安裝不同版本的sdk壤巷、編譯好的虛擬機(jī)系統(tǒng)鏡像等等。為了方便安裝使用瞧毙,sdkmanager的實(shí)現(xiàn)胧华,沒有使用數(shù)據(jù)庫這種強(qiáng)關(guān)系管理工具來管理平臺和依賴,而只是使用環(huán)境變量宙彪、目錄結(jié)構(gòu)這種弱關(guān)系矩动,比如允許ANDROID_SDK_HOME來指定android主目錄,比如sdk版本默認(rèn)放在sdk/platforms下面而虛擬機(jī)系統(tǒng)鏡像放在sdk/system-imges下面释漆。出于靈活性考慮悲没,sdkmanager對sdk版本和系統(tǒng)鏡像版本的目錄結(jié)構(gòu)不是在代碼里寫死的,而是通過package.xml索引文件來聲明的男图,包括包類型示姿,api版本甜橱,顯示名稱,相對路徑等等栈戳。所以當(dāng)我們avdmanager找到了虛擬機(jī)文件的時候渗鬼,還要根據(jù)虛擬機(jī)的平臺、版本荧琼,查看是否有安裝對應(yīng)的系統(tǒng)鏡像譬胎,所以就有了上述的package.xml遍歷。
為了驗(yàn)證這個推測命锄,我們把sdk/platforms/下堰乔,android-21和android-27重命名為android-210,android-270:
heavy:~/Library/Android/sdk/platforms$ ls
android-19 android-22 android-24 android-26
android-210 android-23 android-25 android-270
然后把對應(yīng)目錄下package.xml中的path路徑也修改為210和270,打開sdkmanager,查看安裝的sdk版本:
可以看到修改后的sdk版本還是可以被正確地識別脐恩,說明我們的推測是對的镐侯。但是avdmanager為什么要搜索已安裝的sdk和system-images版本還是沒有看懂。實(shí)際上在虛擬機(jī)目錄下的hardware-qemu.ini文件里驶冒,有關(guān)于虛擬機(jī)的所有配置苟翻,其中就包括了kernel,system等鏡像文件的路徑,是不需要進(jìn)行搜索查找的骗污。
我們總結(jié)一下虛擬機(jī)文件的查找路徑:
- 根據(jù)$ANDROID_SDK_HOME/.android/avd/name.ini獲取虛擬機(jī)列表崇猫,如果環(huán)境變量未設(shè)置,默認(rèn)在用戶的主目錄
- 根據(jù)name.ini里的PATH需忿,查找虛擬機(jī)數(shù)據(jù)的存放路徑
- 虛擬機(jī)使用的數(shù)據(jù)文件路徑诅炉、配置等由路徑下的hardware-qemu.ini文件指定
虛擬機(jī)的文件內(nèi)容
在上面的存儲路徑的研究中,我們已經(jīng)看到了虛擬機(jī)的文件列表屋厘,官方對于這些也沒有非常詳細(xì)的說明涕烧,只說明了幾個比較關(guān)鍵的文件,在這里盡量說明個人理解的所有內(nèi)容汗洒,包括一些推測议纯。
- cache.img
緩存分區(qū),會掛載在虛擬機(jī)的/cache目錄下溢谤,用于存儲下載等一些臨時文件瞻凤,關(guān)機(jī)的時候會被清除,可以在虛擬機(jī)運(yùn)行的時候溯香,使用參數(shù)-cache持久化緩存鲫构。緩存分區(qū)開始是空的,并且會被清空如果使用了-wipe-data選項(xiàng)玫坛。 - cache.img.qcow2
cache.img的軟連接 - config.ini
設(shè)備配置文件,用于Android設(shè)備相關(guān)的屬性包晰。 - data
虛擬機(jī)運(yùn)行的一些數(shù)據(jù) - emulator-user.ini
未知 - encryptionkey.img
不清楚具體作用湿镀,等研究完android源碼構(gòu)建之后炕吸,再行補(bǔ)充。 - encryptionkey.img.qcow2
encryptionkey的軟連接 - hardware-qemu.ini
設(shè)備配置文件勉痴,用于虛擬機(jī)自身相關(guān)的屬性赫模,包括文件系統(tǒng)鏡像等。 - sdcard.img
sd卡分區(qū)蒸矛,用于模擬設(shè)備的sd卡瀑罗。 - sdcard.img.qcow2
sdcard.img的軟連接。 - snapshots
快照數(shù)據(jù)路徑雏掠,用于保存虛擬機(jī)的運(yùn)行狀態(tài)斩祭,快速恢復(fù)虛擬機(jī)。 - system.img.qcow2
system.img的軟連接乡话,因?yàn)閟ystem.img是只讀的摧玫,所以并沒有被拷貝到虛擬機(jī)目錄下。 - userdata.img
系統(tǒng)文件下拷貝過來绑青,用于生成userdata-qemu.img文件的诬像,不知道為啥之后沒有刪除。 - userdata-qemu.img
/data分區(qū)的鏡像文件闸婴,每個虛擬機(jī)單獨(dú)一個坏挠,在虛擬機(jī)創(chuàng)建或者使用-wipe-data選項(xiàng)的時候,根據(jù)系統(tǒng)目錄下的userdata.img文件生成邪乍。 - userdata-qemu.img.qcow2
userdata-qemu.img的軟連接 - version_num.cache
未知
以上是虛擬機(jī)創(chuàng)建后的數(shù)據(jù)文件癞揉,還有幾個文件是放在系統(tǒng)鏡像文件下面,直接通過配置文件索引的:
- kernel-qemu or kernel-ranchu
系統(tǒng)內(nèi)核鏡像 - ramdisk.img
/boot分區(qū)的鏡像溺欧,是system.img之前掛載喊熟,用于做一些初始化工作
虛擬機(jī)的配置
上述已經(jīng)介紹了虛擬機(jī)的兩個配置文件hardware-qemu.ini和config.ini,用于設(shè)置虛擬機(jī)設(shè)備和Android設(shè)備屬性姐刁。一般來說通過這些配置模擬市面上真機(jī)的配置芥牌,不是特殊需要,不要進(jìn)行修改聂使。但是hardware-qemu.ini中指定了虛擬機(jī)的系統(tǒng)分區(qū)鏡像文件的路徑壁拉,如果進(jìn)行源碼調(diào)試,可以直接修改到源碼編譯的目錄柏靶,這樣避免了編譯結(jié)束的拷貝動作弃理。具體的配置請參考配置文件,還是比較直觀的屎蜓。
創(chuàng)建虛擬機(jī)
執(zhí)行以下命令可以創(chuàng)建一個虛擬機(jī):
avdmanager create avd -n TEST_AVD -c 512M -k "system-images;android-23;google_apis;x86_64"
執(zhí)行后avdmanger會讓你輸入虛擬機(jī)的各項(xiàng)配置痘昌,嘗試一路默認(rèn)選項(xiàng)之后,也可以運(yùn)行起來,這里就不再貼了辆苔。這些參數(shù)很多算灸,理解起來也很費(fèi)勁,沒弄好的話后面用起來出啥問題也了不一定驻啤,推薦還是用UI工具來創(chuàng)建虛擬機(jī)會更加方便友好一點(diǎn)菲驴。
這里重點(diǎn)想講一下 -k 參數(shù),一開始沒有研究明白的時候骑冗,總看不懂這個參數(shù)的含義赊瞬,也就看不懂這條命令。這個參數(shù)用分號隔開的贼涩,其實(shí)相對sdk目錄的巧涧,虛擬機(jī)初始鏡像的存放的路徑,用avdmanager UI工具下載的系統(tǒng)鏡像都按照如下的規(guī)則存儲:
~/Library/Android/sdk/system-images/android-apiLevel/variant/arch/
當(dāng)然也可以使用avdmanager命令下載系統(tǒng)鏡像磁携,讀者自行研究哈褒侧。官方教程請戳這里
調(diào)試安卓系統(tǒng)
本來這里想通過查看虛擬機(jī)啟動全部日志,可以查看系統(tǒng)開機(jī)過程谊迄,包括kernel等底層邏輯的闷供,但是嘗試了一下沒有能夠搞定,只能用adb來連接统诺,那就不多介紹了歪脏。