Android虛擬機(jī)使用整理

虛擬機(jī)其實(shí)我們并不陌生,比如vmware,virtualbox等PC平臺(tái)上的虛擬機(jī)軟件,相信很多人都玩過(guò)。比如在windows上跑一個(gè)ubuntu來(lái)研究一下linux,編譯一下android源碼什么的。使用虛擬機(jī)一般有兩個(gè)目的,一個(gè)是平臺(tái)模擬,一個(gè)是便于維護(hù)。

我們知道系統(tǒng)的底層是基于CPU設(shè)計(jì)的硬件平臺(tái),而CPU根據(jù)應(yīng)用場(chǎng)景不同有著不同的指令集和寄存器結(jié)構(gòu),也就是架構(gòu)。常見(jiàn)的PC平臺(tái)有x86、x86_64架構(gòu),移動(dòng)平臺(tái)有arm架構(gòu)。這些架構(gòu)的差異使得不同的系統(tǒng)無(wú)法直接跨平臺(tái)運(yùn)行,因?yàn)橄到y(tǒng)源碼最終要被編譯成機(jī)器指令才能運(yùn)行。當(dāng)然有些系統(tǒng)支持了非常豐富的硬件平臺(tái),比如Linux,既可以在x86平臺(tái)運(yùn)行,也可以在arm平臺(tái)上運(yùn)行。但是如果我們要在x86平臺(tái)上運(yùn)行最初只支持arm結(jié)構(gòu)的Android系統(tǒng),我們就需要一個(gè)中間層,模擬出一個(gè)arm平臺(tái),將arm架構(gòu)的指令和寄存器操作,使用軟件功能來(lái)實(shí)現(xiàn)。有的虛擬機(jī)為了提高執(zhí)行效率,也有將目標(biāo)平臺(tái)的指令,直接翻譯成宿主平臺(tái)指令執(zhí)行的,具體一點(diǎn)可以參考這里,虛擬機(jī)的原理大致如下圖:

虛擬機(jī)原理示意圖

我們說(shuō)的平臺(tái)模擬的虛擬機(jī)就屬于TYPE2類型,底層的OS就是宿主系統(tǒng),上層的OS就是目標(biāo)系統(tǒng),中間的HYPER VISOR就是虛擬機(jī)軟件。Android虛擬機(jī)也是平臺(tái)虛擬機(jī)的一種,為了后續(xù)方便研究,我們先總結(jié)一下PC上虛擬機(jī)的運(yùn)行過(guò)程。首先我們需要下載一個(gè)虛擬機(jī)軟件,然后在軟件里創(chuàng)建虛擬機(jī),指定虛擬的硬件配置,虛擬機(jī)數(shù)據(jù)會(huì)存儲(chǔ)在一個(gè)虛擬磁盤文件里,最后我們需要為虛擬機(jī)安裝目標(biāo)系統(tǒng),然后就可以啟動(dòng)虛擬機(jī)了。所以它包含幾個(gè)主要部分:

  • 虛擬機(jī)軟件
  • 硬件配置
  • 虛擬機(jī)文件
  • 目標(biāo)系統(tǒng)

虛擬機(jī)和真機(jī)差異

我們運(yùn)行自己APP,既可選擇真機(jī)調(diào)試運(yùn)行,也可以使用虛擬機(jī)。一般情況下,使用真機(jī)速度更快,體驗(yàn)更佳,但是真機(jī)也有自己的一些缺陷。比如,獲取成本高,假如要測(cè)試APP的兼容性,針對(duì)不同API版本的機(jī)型都進(jìn)行配置的話,將會(huì)是一筆不少的花費(fèi)。其次,真機(jī)的系統(tǒng)是經(jīng)過(guò)OEM廠家定制的,不一定是原生的API和內(nèi)在邏輯,也無(wú)法針對(duì)GoogleAPI等服務(wù)進(jìn)行開(kāi)發(fā),如果機(jī)器里沒(méi)有的話。特別地,當(dāng)你想要燒錄自己編譯的源碼,對(duì)FrameWork或者更加底層的邏輯行調(diào)試的時(shí)候,真機(jī)則更加不方便。OEM廠家一般都會(huì)鎖定刷機(jī)功能,比如FastBoot,只能用廠家自己的刷機(jī)工具進(jìn)行刷機(jī),一般也不容獲取。并且每家的源碼和鏡像文件格式都不相同,編譯的標(biāo)準(zhǔn)源碼無(wú)法直接燒錄。所以,如果要進(jìn)行Android系統(tǒng)學(xué)習(xí),學(xué)習(xí)虛擬機(jī)的使用還是非常必要的。

使用方式及差異

Android虛擬機(jī)的獲取方式有兩種,一種是下載AndroidStudio,使用AndroidStudio進(jìn)行虛擬機(jī)的創(chuàng)建,下載對(duì)應(yīng)的系統(tǒng)鏡像,虛擬機(jī)運(yùn)行之后就可以當(dāng)做設(shè)備一樣使用adb連接調(diào)試了。還有一種是下載Android sdk tool,使用avd manager命令進(jìn)行安裝、運(yùn)行。第一種的優(yōu)勢(shì)是方便、直觀,根據(jù)UI提示一步一步操作就可以創(chuàng)建、運(yùn)行虛擬機(jī),實(shí)際上它也是使用avd manager的功能。第二種的優(yōu)勢(shì)是可以了解adv manager的工作原理,可以使用參數(shù)進(jìn)行最靈活的配置,可以定制自己的kernel、system鏡像等等。我們要研究的就是第二種。

從官方的教程來(lái)看,Android虛擬機(jī)是基于QEMU開(kāi)源虛擬機(jī)衍生出來(lái)的,如果想深入研究一下虛擬機(jī)原理,可以研究一下QEMU。如果要看官方教程請(qǐng)戳這里,QEMU官方教程請(qǐng)戳這里。

虛擬機(jī)的安裝

官方鏈接上,我們可以下載到不同平臺(tái)的AndroidStudio,其中自帶了整套的AndroidSdk工具。如果我們不需要AndroidStudio,比如在構(gòu)建服務(wù)器上,我們也可以只下載sdk工具,通過(guò)工具在線下載build tool、不同版本的sdk、創(chuàng)建虛擬機(jī)等等。這里不做詳細(xì)的介紹。

安裝完之后,將SDK路徑/tools添加到PATH環(huán)境變量中,便于使用。也可以直接到sdk路徑下面運(yùn)行命令,MAC平臺(tái)的SDK路徑是:

/Users/your_name/Library/Android/sdk/

虛擬機(jī)的運(yùn)行

使用avdmanager命令行創(chuàng)建虛擬機(jī)比較復(fù)雜,也是理解虛擬機(jī)的核心部分,我們稍后一步一步探索。

假如我們有一個(gè)創(chuàng)建好的虛擬機(jī),比如我們可以先使用AndroidStudio創(chuàng)建一個(gè),就可以使用以下的命令直接運(yùn)行虛擬機(jī)。

emulator -avd avd_name [ {-option [value]} … ]

或者

emulator @avd_name [ {-option [value]} … ]

可以看到這個(gè)emulator就是我們虛擬機(jī)程序正主了,我們到sdk安裝目錄下去看看它是個(gè)什么文件。

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是一個(gè)可執(zhí)行文件,但是體積很小,按照虛擬機(jī)的功能和復(fù)雜性而言,應(yīng)該是不只這個(gè)大小的,所以合理推測(cè)它應(yīng)該還依賴了其他的可執(zhí)行文件或者庫(kù)文件,這里先不研究,就把它當(dāng)做虛擬機(jī)程序。

虛擬機(jī)的存儲(chǔ)路徑

根據(jù)PC虛擬機(jī)使用總結(jié),虛擬機(jī)要運(yùn)行還需要安裝了目標(biāo)系統(tǒng)的虛擬磁盤文件,這里通過(guò)-avd參數(shù)的名稱指定了磁盤文件位置,我們來(lái)看看虛擬機(jī)是怎么根據(jù)名稱來(lái)找到這個(gè)文件的。

我們執(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解析了很多的平臺(tái)配置文件:package.xml,最后告訴我們找到一個(gè)可用的虛擬機(jī),位置在主目錄下面。我們打開(kāi)文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)它只是一個(gè)配置文件,通過(guò)path和path.rel指向虛擬機(jī)數(shù)據(jù)文件夾的絕對(duì)、相對(duì)地址。我們?cè)俨榭茨夸浀膬?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)容稍后研究?,F(xiàn)在我們回頭來(lái)看看,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主目錄,可以通過(guò)ANDROID_SDK_HOME來(lái)動(dòng)態(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)境變量指向一個(gè)其他的路徑,avdmanager就會(huì)到指定的路徑下去尋找虛擬機(jī)文件,下面這個(gè)api26是手動(dòng)拷貝到環(huán)境變量指定的路徑下面的,但是它會(huì)被當(dāng)做一個(gè)虛擬機(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ī)存儲(chǔ)路徑的名稱:

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ī),但是加載的時(shí)候會(huì)失敗,如果我們修改了虛擬機(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:

說(shuō)明實(shí)際上avdmanager是根據(jù)路徑規(guī)則,找的同名配置文件,然后根據(jù)配置文件里的路徑,再尋找虛擬機(jī)的數(shù)據(jù)路徑的。那為什么要解析sdk路徑下面的package.xml文件呢?

這里我沒(méi)有找到足夠的資料,只能做一個(gè)推測(cè)。我們知道AndroidSDK工具分了很多平臺(tái)和api版本,除去最基本的tools,plaform-tools等工具是sdk必備的,其他的都是可以根據(jù)需要選裝的,所以google提供了sdkmanager作為一個(gè)工具,用于管理本地的sdk工具集,包括從遠(yuǎn)程倉(cāng)庫(kù)下載和安裝不同版本的sdk、編譯好的虛擬機(jī)系統(tǒng)鏡像等等。為了方便安裝使用,sdkmanager的實(shí)現(xiàn),沒(méi)有使用數(shù)據(jù)庫(kù)這種強(qiáng)關(guān)系管理工具來(lái)管理平臺(tái)和依賴,而只是使用環(huán)境變量、目錄結(jié)構(gòu)這種弱關(guān)系,比如允許ANDROID_SDK_HOME來(lái)指定android主目錄,比如sdk版本默認(rèn)放在sdk/platforms下面而虛擬機(jī)系統(tǒng)鏡像放在sdk/system-imges下面。出于靈活性考慮,sdkmanager對(duì)sdk版本和系統(tǒng)鏡像版本的目錄結(jié)構(gòu)不是在代碼里寫死的,而是通過(guò)package.xml索引文件來(lái)聲明的,包括包類型,api版本,顯示名稱,相對(duì)路徑等等。所以當(dāng)我們avdmanager找到了虛擬機(jī)文件的時(shí)候,還要根據(jù)虛擬機(jī)的平臺(tái)、版本,查看是否有安裝對(duì)應(yīng)的系統(tǒng)鏡像,所以就有了上述的package.xml遍歷。

為了驗(yàn)證這個(gè)推測(cè),我們把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

然后把對(duì)應(yīng)目錄下package.xml中的path路徑也修改為210和270,打開(kāi)sdkmanager,查看安裝的sdk版本:

路徑修改后sdk版本列表

可以看到修改后的sdk版本還是可以被正確地識(shí)別,說(shuō)明我們的推測(cè)是對(duì)的。但是avdmanager為什么要搜索已安裝的sdk和system-images版本還是沒(méi)有看懂。實(shí)際上在虛擬機(jī)目錄下的hardware-qemu.ini文件里,有關(guān)于虛擬機(jī)的所有配置,其中就包括了kernel,system等鏡像文件的路徑,是不需要進(jìn)行搜索查找的。

我們總結(jié)一下虛擬機(jī)文件的查找路徑:

  1. 根據(jù)$ANDROID_SDK_HOME/.android/avd/name.ini獲取虛擬機(jī)列表,如果環(huán)境變量未設(shè)置,默認(rèn)在用戶的主目錄
  2. 根據(jù)name.ini里的PATH,查找虛擬機(jī)數(shù)據(jù)的存放路徑
  3. 虛擬機(jī)使用的數(shù)據(jù)文件路徑、配置等由路徑下的hardware-qemu.ini文件指定

虛擬機(jī)的文件內(nèi)容

在上面的存儲(chǔ)路徑的研究中,我們已經(jīng)看到了虛擬機(jī)的文件列表,官方對(duì)于這些也沒(méi)有非常詳細(xì)的說(shuō)明,只說(shuō)明了幾個(gè)比較關(guān)鍵的文件,在這里盡量說(shuō)明個(gè)人理解的所有內(nèi)容,包括一些推測(cè)。

  • cache.img
    緩存分區(qū),會(huì)掛載在虛擬機(jī)的/cache目錄下,用于存儲(chǔ)下載等一些臨時(shí)文件,關(guān)機(jī)的時(shí)候會(huì)被清除,可以在虛擬機(jī)運(yùn)行的時(shí)候,使用參數(shù)-cache持久化緩存。緩存分區(qū)開(kāi)始是空的,并且會(huì)被清空如果使用了-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是只讀的,所以并沒(méi)有被拷貝到虛擬機(jī)目錄下。
  • userdata.img
    系統(tǒng)文件下拷貝過(guò)來(lái),用于生成userdata-qemu.img文件的,不知道為啥之后沒(méi)有刪除。
  • userdata-qemu.img
    /data分區(qū)的鏡像文件,每個(gè)虛擬機(jī)單獨(dú)一個(gè),在虛擬機(jī)創(chuàng)建或者使用-wipe-data選項(xiàng)的時(shí)候,根據(jù)系統(tǒng)目錄下的userdata.img文件生成。
  • userdata-qemu.img.qcow2
    userdata-qemu.img的軟連接
  • version_num.cache
    未知

以上是虛擬機(jī)創(chuàng)建后的數(shù)據(jù)文件,還有幾個(gè)文件是放在系統(tǒng)鏡像文件下面,直接通過(guò)配置文件索引的:

  • kernel-qemu or kernel-ranchu
    系統(tǒng)內(nèi)核鏡像
  • ramdisk.img
    /boot分區(qū)的鏡像,是system.img之前掛載,用于做一些初始化工作

虛擬機(jī)的配置

上述已經(jīng)介紹了虛擬機(jī)的兩個(gè)配置文件hardware-qemu.ini和config.ini,用于設(shè)置虛擬機(jī)設(shè)備和Android設(shè)備屬性。一般來(lái)說(shuō)通過(guò)這些配置模擬市面上真機(jī)的配置,不是特殊需要,不要進(jìn)行修改。但是hardware-qemu.ini中指定了虛擬機(jī)的系統(tǒng)分區(qū)鏡像文件的路徑,如果進(jìn)行源碼調(diào)試,可以直接修改到源碼編譯的目錄,這樣避免了編譯結(jié)束的拷貝動(dòng)作。具體的配置請(qǐng)參考配置文件,還是比較直觀的。

創(chuàng)建虛擬機(jī)

執(zhí)行以下命令可以創(chuàng)建一個(gè)虛擬機(jī):

avdmanager create avd -n TEST_AVD -c 512M -k "system-images;android-23;google_apis;x86_64"

執(zhí)行后avdmanger會(huì)讓你輸入虛擬機(jī)的各項(xiàng)配置,嘗試一路默認(rèn)選項(xiàng)之后,也可以運(yùn)行起來(lái),這里就不再貼了。這些參數(shù)很多,理解起來(lái)也很費(fèi)勁,沒(méi)弄好的話后面用起來(lái)出啥問(wèn)題也了不一定,推薦還是用UI工具來(lái)創(chuàng)建虛擬機(jī)會(huì)更加方便友好一點(diǎn)。

這里重點(diǎn)想講一下 -k 參數(shù),一開(kāi)始沒(méi)有研究明白的時(shí)候,總看不懂這個(gè)參數(shù)的含義,也就看不懂這條命令。這個(gè)參數(shù)用分號(hào)隔開(kāi)的,其實(shí)相對(duì)sdk目錄的,虛擬機(jī)初始鏡像的存放的路徑,用avdmanager UI工具下載的系統(tǒng)鏡像都按照如下的規(guī)則存儲(chǔ):

~/Library/Android/sdk/system-images/android-apiLevel/variant/arch/

當(dāng)然也可以使用avdmanager命令下載系統(tǒng)鏡像,讀者自行研究哈。官方教程請(qǐng)戳這里

調(diào)試安卓系統(tǒng)

本來(lái)這里想通過(guò)查看虛擬機(jī)啟動(dòng)全部日志,可以查看系統(tǒng)開(kāi)機(jī)過(guò)程,包括kernel等底層邏輯的,但是嘗試了一下沒(méi)有能夠搞定,只能用adb來(lái)連接,那就不多介紹了。

?著作權(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)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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