一般程序的啟動步驟,可以用下圖描述。程序由內核加載分析,使用linker鏈接需要的共享庫,然後從c運行庫的入口開始執行。
通常,native進程是由shell或者init啟動,啟動的過程如下:
需要注意的是,Android bionic提供的加載器是/system/bin/linker,而普通linux系統用的glibc是/lib/ld-linux-xx.so.2。這也是為何其他linux平台同指令架構的二進制文件,不能在android上運行的原因之一:啟動用戶進程的加載器這個程序運行的第一步就出錯了。
Java進程的啟動比較特殊,Java進程是zygote啟動的,zygote在folk進程之後,並沒有執行execve指令,因此是共享了zygote的代碼段和數據段。其它的java進程,可以看做都是zygote的克隆,克隆之後的進程,各自根再據自己的需求(java代碼),解釋java語言。
也就是說:Android的所有進程,從native角度看都是zygote。 其對應的程序都是 /system/bin/app_process,虛擬機是運行在其中的。
那為何java進程又如此的不同呢? 實際上,從native的角度看,不同的各種java程序,可以如此理解:只是/system/bin/app_process 這個程序,因為不同的輸入(Java dex字節碼)而引起的。
上圖中,user APK實際上市zygote的一個克隆(啟動->進入main等之前的流程沒有畫出, app進程沒有這個步驟,是從zygote進程中克隆過來),差別主要在dvm虛擬機執行的java代碼的不同導致的表現的行為差異巨大。
Java進程沒有執行exec調用,這樣有一個很大的好處:使用linux的COW(copy on Write)技術,就可以在多個java進程間,共享內存資源——主要是java的核心庫。
Java程序也可以使用native庫,此時的native庫需要通過dlopen來打開(即java中,使用System.loadLibrary()方法加載so庫,虛擬機對應會調用的C庫方法),dlopen加載so庫的過程中,依舊會通過linker分析處理so庫的elf信息,加載其它依賴的動態庫。
(注:zygote實際上是/system/bin/app_process,zygote只是app_process的別名)
更多Android相關信息見Android 專題頁面 http://www.linuxidc.com/topicnews.aspx?tid=11