本章主要內容 html
· 深刻分析zygote,並介紹system_server進程的初始化工做。 java
本章涉及的源代碼文件名及位置 android
下面是咱們本章分析的源碼文件名及其位置。 shell
· App_main.cpp 編程
framework/base/cmds/app_process/App_main.cpp 數組
· AndroidRuntime.h 網絡
framework/base/include/android_runtime/AndroidRuntime.h app
· android_debug_JNITest.cpp 異步
framework/base/core/jni/android_debug_JNITest.cpp socket
· ZygoteInit.java
framework/base/core/java/com/android/internal/os/ZygoteInit.java
· dalvik_system_Zygote.c
dalvik/vm/native/dalvik_system_Zygote.c
· RuntimeInit.java
framework/base/core/java/com/android/internal/os/RuntimeInit.java
· SystemServer.java
framework/base/services/java/com/android/server/SystemServer.java
· com_android_server_SystemServer.cpp
framework/base/services/jni/com_android_server_SystemServer.cpp
· system_init.cpp
framework/base/cmds/system_server/library/system_init.cpp
· Watchdog.java
framework/base/services/java/com/android/server/Watchdog.java
· ActivityManagerService.java
framework/base/services/java/com/android/server/am/ActivityManagerService.java
· Process.java
framework/base/core/java/android/os/Process.java
· ZygoteConnection.java
framework/base/core/java/com/android/internal/os/ZygoteConnection.java
讀者可能已經知道,Android系統存在着兩個徹底不一樣的世界:
· Java世界,Google放出的SDK主要就是針對這個世界的。在這個世界中運行的程序都是基於Dalvik虛擬機的Java程序。
· Native世界,也就是用Native語言C或C++開發的程序,它們組成了Native世界。
初次接觸Android的人,可能會有幾個疑問:
· Android是基於Linux內核構建的,它最先存在的確定是Native世界,那麼Java世界是何時建立的呢?
· 咱們都知道,程序運行時必定要有一個進程,可是咱們在編寫Activity、Service的時候卻絕少接觸到「進程」這一律念。固然這是Google有意爲之,但這些Activity或Service卻又不能脫離「進程」而存在。那麼,這個「進程」是怎麼建立和運行的呢?這是一個值得琢磨的問題。
· 在程序中,咱們常用系統的Service,那麼,這些Service在哪裏呢?
這些問題的答案都和咱們本章的兩位主人公zygote和system_server有關。zygote這個詞的中文意思是「受精卵」,它和Android系統中的Java世界有着重要關係。而system_server則「人如其名」,系統中重要的service都駐留於Java中。
zygote和system_server這兩個進程分別是Java世界的半邊天,任何一個進程的死亡,都會致使Java世界的崩潰,夠厲害吧?下面咱們就來見識見識這兩個重量級人物。
Zygote自己是一個Native的應用程序,和驅動、內核等均無關係。根據第3章對於init的介紹咱們能夠知道,Zygote是由init進程根據init.rc文件中的配置項而建立的。在分析它以前,咱們有必要先簡單介紹一下「zygote」這個名字的來歷。zygote最初的名字叫「app_process」,這個名字是在Android.mk文件中被指定的,但app_process在運行過程當中,經過Linux下的pctrl系統調用將本身的名字換成了「zygote」,因此咱們經過ps命令看到的進程名是「zygote」。
zygote玩的這一套「換名把戲」並不影響咱們的分析,它的原型app_process所對應的源文件是App_main.cpp,代碼以下所示:
[-->App_main.cpp]
int main(int argc, const char* const argv[])
{
/*
Zygote進程由init經過fork而來,咱們回顧一下init.rc中設置的啓動參數:
-Xzygote/system/bin --zygote --start-system-server
*/
mArgC= argc;
mArgV= argv;
mArgLen = 0;
for(int i=0; i<argc; i++) {
mArgLen += strlen(argv[i]) + 1;
}
mArgLen--;
AppRuntime runtime;
// 調用Appruntime的addVmArguments,這個函數很簡單,讀者能夠自行分析
int i= runtime.addVmArguments(argc, argv);
if (i< argc) {
//設置runtime的mParentDir爲/system/bin
runtime.mParentDir = argv[i++];
}
if (i< argc) {
arg = argv[i++];
if(0 == strcmp("--zygote", arg)) {
//咱們傳入的參數知足if的條件,並且下面的startSystemServer的值爲true
bool startSystemServer = (i < argc) ?
strcmp(argv[i],"--start-system-server") == 0 : false;
setArgv0(argv0, "zygote");
//設置本進程名爲zygote,這正是前文所講的「換名把戲」。
set_process_name("zygote");
//①調用runtime的start,注意第二個參數startSystemServer爲true
runtime.start("com.android.internal.os.ZygoteInit",
startSystemServer);
}
......
}
......
}
Zygote的這個main函數雖很簡單,但其重要功能倒是由AppRuntime的start來完成的。下面,咱們就來具體分析這個AppRuntime。
AppRuntime類的聲明和實現均在App_main.cpp中,它是從AndroidRuntime類派生出來的,圖4-1顯示了這兩個類的關係和一些重要函數:
圖4-1 AppRuntime和AndroidRuntime的關係
由上圖咱們可知:
· AppRuntime重載了onStarted、onZygoteInit和onExit函數。
前面的代碼中調用了AndroidRuntime的start函數,由圖4-1可知,這個start函數使用的是基類AndroidRuntime的start,咱們來分析一下它,注意它的調用參數。
[-->AndroidRuntime.cpp]
void AndroidRuntime::start(const char*className, const bool startSystemServer)
{
//className的值是"com.android.internal.os.ZygoteInit"
//startSystemServer的值是true
char*slashClassName = NULL;
char*cp;
JNIEnv* env;
blockSigpipe();//處理SIGPIPE信號
......
constchar* rootDir = getenv("ANDROID_ROOT");
if (rootDir == NULL) {
//若是環境變量中沒有ANDROID_ROOT,則新增該變量,並設置值爲「/system"
rootDir = 「/system";
......
setenv("ANDROID_ROOT", rootDir, 1);
}
//① 建立虛擬機
if(startVm(&mJavaVM, &env) != 0)
goto bail;
//②註冊JNI函數
if(startReg(env) < 0) {
goto bail;
}
jclassstringClass;
jobjectArray strArray;
jstring classNameStr;
jstring startSystemServerStr;
stringClass = env->FindClass("java/lang/String");
//建立一個有兩個元素的String數組,即Java代碼 String strArray[] = new String[2]
strArray = env->NewObjectArray(2, stringClass, NULL);
classNameStr = env->NewStringUTF(className);
//設置第一個元素爲"com.android.internal.os.ZygoteInit"
env->SetObjectArrayElement(strArray, 0, classNameStr);
startSystemServerStr = env->NewStringUTF(startSystemServer ?
"true" : "false");
//設置第二個元素爲"true",注意這兩個元素都是String類型,即字符串。
env->SetObjectArrayElement(strArray, 1, startSystemServerStr);
jclassstartClass;
jmethodID startMeth;
slashClassName = strdup(className);
/*
將字符串「com.android.internal.os.ZygoteInit」中的「. 」換成「/」,
這樣就變成了「com/android/internal/os/ZygoteInit」,這個名字符合JNI規範,
咱們可將其簡稱爲ZygoteInit類。
*/
for(cp = slashClassName; *cp != '\0'; cp++)
if(*cp == '.')
*cp = '/';
startClass = env->FindClass(slashClassName);
......
//找到ZygoteInit類的static main函數的jMethodId。
startMeth = env->GetStaticMethodID(startClass, "main",
"([Ljava/lang/String;)V");
......
/*
③經過JNI調用Java函數,注意調用的函數是main,所屬的類是
com.android.internal.os.ZygoteInit,傳遞的參數是
「com.android.internal.os.ZygoteInit true」,
調用ZygoteInit的main函數後,Zygote便進入了Java世界!
也就是說,Zygote是開創Android系統中Java世界的盤古。
*/
env->CallStaticVoidMethod(startClass,startMeth, strArray);
//Zygote退出,在正常狀況下,Zygote不須要退出。
if(mJavaVM->DetachCurrentThread() != JNI_OK)
LOGW("Warning: unable to detach main thread\n");
if(mJavaVM->DestroyJavaVM() != 0)
LOGW("Warning: VM did not shut down cleanly\n");
bail:
free(slashClassName);
}
經過上面的分析,咱們找到了三個關鍵點,它們共同組成了開創Android系統中Java世界的三部曲。如今讓咱們來具體地觀察它們。
咱們先看三部曲中的第一部:startVm,這個函數沒有特別之處,就是調用JNI的虛擬機建立函數,可是虛擬機建立時的一些參數倒是在startVm中被肯定的,其代碼以下所示:
[-->AndroidRuntime.cpp]
int AndroidRuntime::startVm(JavaVM** pJavaVM,JNIEnv** pEnv)
{
//這個函數絕大部分代碼都是設置虛擬機的參數,咱們只分析其中的兩個。
/*
下面的代碼是用來設置JNI check選項的。JNIcheck 指的是Native層調用JNI函數時,
系統所作的一些檢查工做。例如調用NewUTFString函數時,系統會檢查傳入的字符串是否是符合
UTF-8的要求。JNI check還能檢查資源是否正確釋放。但這個選項也有其反作用,好比:
1)由於檢查工做比較耗時,因此會影響系統運行速度。
2)有些檢查過於嚴格,例如上面的字符串檢查,一旦出錯,則調用進程就會abort。
因此,JNI check選項通常只在調試的eng版設置,而正式發佈的user版則不設置該選項。
下面這幾句代碼就控制着是否啓用JNI check,這是由系統屬性決定的,eng版如通過特殊配置,也能夠去掉JNI check。
*/
property_get("dalvik.vm.checkjni",propBuf, "");
if(strcmp(propBuf, "true") == 0) {
checkJni = true;
} elseif (strcmp(propBuf, "false") != 0) {
property_get("ro.kernel.android.checkjni",propBuf, "");
if(propBuf[0] == '1') {
checkJni = true;
}
}
......
/*
設置虛擬機heapsize,默認爲16MB。絕大多數廠商都會修改這個值,通常是32MB。
heapsize不能設置太小,不然在操做大尺寸的圖片時沒法分配所需內存。
這裏有一個問題,即heapsize既然是系統級的屬性,那麼可否根據不一樣應用程序的需求來進行動
態調整?我開始也考慮過可否實現這一構想,不過但願很快就破滅了。對這一問題,咱們將在拓展
部分深刻討論。
*/
strcpy(heapsizeOptsBuf, "-Xmx");
property_get("dalvik.vm.heapsize", heapsizeOptsBuf+4, "16m");
opt.optionString = heapsizeOptsBuf;
mOptions.add(opt);
if(checkJni) {
opt.optionString ="-Xcheck:jni";
mOptions.add(opt);
//JNIcheck中的資源檢查,系統中建立的Globalreference個數不能超過2000
opt.optionString = "-Xjnigreflimit:2000";
mOptions.add(opt);
}
// 調用JNI_CreateJavaVM建立虛擬機,pEnv返回當前線程的JNIEnv變量
if(JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
LOGE("JNI_CreateJavaVM failed\n");
goto bail;
}
result= 0;
bail:
free(stackTraceFile);
returnresult;
}
關於dalvik虛擬機的詳細參數,讀者能夠參見Dalvik/Docs/Dexopt.html中的說明。這個Docs目錄下的內容,或許可幫助咱們更深刻地瞭解dalvik虛擬機。
前面已經介紹瞭如何建立虛擬機,下一步則須要給這個虛擬機註冊一些JNI函數。正是由於後續Java世界用到的一些函數是採用native方式來實現的,因此才必須提早註冊這些函數。
下面咱們來看看這個startReg函數,代碼以下所示:
[-->AndroidRuntime.cpp]
int AndroidRuntime::startReg(JNIEnv* env)
{
//注意,設置Thread類的線程建立函數爲javaCreateThreadEtc
//它的做用,將在對Thread分析一部分(第5章)中作詳細介紹。
androidSetCreateThreadFunc((android_create_thread_fn)javaCreateThreadEtc);
env->PushLocalFrame(200);
//註冊jni函數,gRegJNI是一個全局數組。
if(register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
env->PopLocalFrame(NULL);
return -1;
}
env->PopLocalFrame(NULL);
//下面這句話應當是「碼農」休閒時的小把戲。在突飛猛進的IT世界中,它現已絕對是「文物」了。
//createJavaThread("fubar", quickTest, (void*)"hello");
return0;
}
咱們來看看register_jni_procs,代碼以下所示:
[-->AndroidRuntime.cpp]
static int register_jni_procs(const RegJNIRecarray[], size_t count, JNIEnv* env)
{
for(size_t i = 0; i < count; i++) {
if(array[i].mProc(env) < 0) {//僅僅是一個封裝,調用數組元素的mProc函數
return -1;
}
}
return 0;
}
上面的函數調用的不過是數組元素的mProc函數,再讓咱們直接看看這個全局數組的gRegJNI變量。
[-->AndroidRuntime.cpp::gRegJNI聲明]
static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_debug_JNITest),
REG_JNI(register_com_android_internal_os_RuntimeInit),
REG_JNI(register_android_os_SystemClock),
REG_JNI(register_android_util_EventLog),
REG_JNI(register_android_util_Log),
...//共有100項
};
REG_JNI是一個宏,宏裏邊包括的就是那個mProc函數,這裏,咱們來分析一個例子。
[-->android_debug_JNITest.cpp]
int register_android_debug_JNITest(JNIEnv* env)
{
//爲android.debug.JNITest類註冊它所須要的JNI函數
returnjniRegisterNativeMethods(env, "android/debug/JNITest",
gMethods,NELEM(gMethods));
}
哦,原來mProc就是爲Java類註冊JNI函數!
至此,虛擬機已建立好,JNI函數也已註冊,下一步就要分析CallStaticVoidMethod了。經過這個函數,咱們將進入Android所精心打造的Java世界,並且最佳狀況是,永遠也不回到Native世界。
這個Java世界的入口在哪裏?根據前面的分析,CallStaticVoidMethod最終將調用com.android.internal.os.ZygoteInit的main函數,下面就來看看這個入口函數。代碼以下所示:
[-->ZygoteInit.java]
public static void main(String argv[]) {
try {
SamplingProfilerIntegration.start();
//①註冊Zygote用的socket
registerZygoteSocket();
//②預加載類和資源
preloadClasses();
preloadResources();
......
// 強制一次垃圾收集
gc();
//咱們傳入的參數知足if分支
if (argv[1].equals("true")) {
startSystemServer();//③啓動system_server進程
}else if (!argv[1].equals("false")) {
thrownew RuntimeException(argv[0] + USAGE_STRING);
}
// ZYGOTE_FORK_MODE被定義爲false,因此知足else的條件
if(ZYGOTE_FORK_MODE) {
runForkMode();
}else {
runSelectLoopMode();//④zygote調用這個函數
}
closeServerSocket();//關閉socket
}catch (MethodAndArgsCaller caller) {
caller.run();//⑤很重要的caller run函數,之後分析
}catch (RuntimeException ex) {
closeServerSocket();
throw ex;
}
......
}
在ZygoteInit的main函數中,咱們列舉出了5大關鍵點,下面對其一一進行分析。先看第一點:registerZygoteSocket。
Zygote以及系統中其餘程序的通訊沒有使用Binder,而是採用了基於AF_UNIX類型的Socket。registerZygoteSocket函數的使命正是創建這個Socket。代碼以下所示:
[-->ZygoteInit.java]
private static void registerZygoteSocket() {
if(sServerSocket == null) {
intfileDesc;
try{
//從環境變量中獲取Socket的fd,還記得第3章init中介紹的zygote是如何啓動的嗎?
//這個環境變量由execv傳入。
String env = System.getenv(ANDROID_SOCKET_ENV);
fileDesc = Integer.parseInt(env);
}
try{
//建立服務端Socket,這個Socket將listen並accept Client
sServerSocket= new LocalServerSocket(createFileDescriptor(fileDesc));
}
}
}
registerZygoteSocket很簡單,就是建立一個服務端的Socket。不過讀者應該提早想到下面兩個問題:
· 誰是客戶端?
· 服務端會怎麼處理客戶端的消息?
建議:讀者要好好學習與Socket相關的知識,這些知識對網絡編程或簡單的IPC使用,是會有幫助的。
如今咱們要分析的就是preloadClasses和preloadResources函數了。先來看看preloadClasses。
[-->ZygoteInit.java]
private static void preloadClasses() {
finalVMRuntime runtime = VMRuntime.getRuntime();
//預加載類的信息存儲在PRELOADED_CLASSES變量中,它的值爲"preloaded-classes"
InputStream is = ZygoteInit.class.getClassLoader().getResourceAsStream(
PRELOADED_CLASSES);
if(is == null) {
Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES +".");
}else {
...... //作一些統計和準備工做
try {
BufferedReader br
= new BufferedReader(newInputStreamReader(is), 256);
//讀取文件的每一行,忽略#開頭的註釋行
int count = 0;
String line;
String missingClasses = null;
while ((line = br.readLine()) != null) {
line = line.trim();
if(line.startsWith("#") || line.equals("")) {
continue;
}
try {
//經過Java反射來加載類,line中存儲的是預加載的類名
Class.forName(line);
......
count++;
} catch(ClassNotFoundException e) {
......
} catch (Throwable t) {
......
}
}
...... //掃尾工做
}
}
preloadClasses看起來是如此簡單,可是你知道它有多少個類須要預先加載嗎?
用coolfind在framework中搜索名爲「preloaded-classes」的文件,最後會在framework/base目錄下找到。它是一個文本文件,內容以下:
# Classes which are preloaded bycom.android.internal.os.ZygoteInit.
# Automatically generated by
# frameworks/base/tools/preload/WritePreloadedClassFile.java.
# MIN_LOAD_TIME_MICROS=1250 //超時控制
android.R$styleable
android.accounts.AccountManager
android.accounts.AccountManager$4
android.accounts.AccountManager$6
android.accounts.AccountManager$AmsTask
android.accounts.AccountManager$BaseFutureTask
android.accounts.AccountManager$Future2Task
android.accounts.AuthenticatorDescription
android.accounts.IAccountAuthenticatorResponse$Stub
android.accounts.IAccountManager$Stub
android.accounts.IAccountManagerResponse$Stub
......//一共有1268行
這個preload-class一共有1268行,試想,加載這麼多類得花多少時間!
說明:preload_class文件由framework/base/tools/preload工具生成,它須要判斷每一個類加載的時間是否大於1250微秒,超過這個時間的類就會被寫到preload-classes文件中,最後由zygote預加載。這方面的內容,讀者可參考有關preload工具中的說明,這裏就再也不贅述。
preloadClass函數的執行時間比較長,這是致使Android系統啓動慢的緣由之一。對這一塊能夠作一些優化,但優化是基於對整個系統有比較深刻了解才能實現的。
注意:在拓展思考部分中,咱們會討論Android啓動速度問題。
preloadResources和preloadClass相似,它主要是加載framework-res.apk中的資源。這裏就再也不介紹它了。
說明:在UI編程中常使用的com.android.R.XXX資源,是系統默認的資源,它們就是由Zygote加載的。
咱們如今要分析的是第三個關鍵點:startSystemServer。這個函數會建立Java世界中系統Service所駐留的進程system_server,該進程是framework的核心。若是它死了,就會致使zygote自殺。先來看看這個核心進程是如何啓動的。
[-->ZygoteInit.java]
private static boolean startSystemServer()
throws MethodAndArgsCaller, RuntimeException {
//設置參數
String args[] = {
"--setuid=1000",//uid和gid等設置
"--setgid=1000",
"--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,
3001,3002,3003",
"--capabilities=130104352,130104352",
"--runtime-init",
"--nice-name=system_server", //進程名,叫system_server
"com.android.server.SystemServer", //啓動的類名
};
ZygoteConnection.Arguments parsedArgs = null;
int pid;
try {
//把上面字符串數組參數轉換成Arguments對象。具體內容請讀者自行分析。
parsedArgs = new ZygoteConnection.Arguments(args);
int debugFlags = parsedArgs.debugFlags;
//fork一個子進程,看來,這個子進程就是system_server進程。
pid = Zygote.forkSystemServer(
parsedArgs.uid,parsedArgs.gid,
parsedArgs.gids,debugFlags, null);
}catch (IllegalArgumentException ex) {
throw new RuntimeException(ex);
}
/*
關於fork的知識,請讀者務花些時間去研究。若是對fork具體實現還感興趣,可參考
《Linux內核源代碼情景分析》一書。(該書由浙江大學出版社出版,做者爲毛德操、胡希明)
下面代碼中,若是pid爲零,則表示處於子進程中,也就是處於system_server進程中。
*/
if(pid == 0) {
//① system_server進程的工做
handleSystemServerProcess(parsedArgs);
}
//zygote返回true
return true;
}
OK,這裏出現了一個分水嶺,即Zygote進行了一次無性繁殖,分裂出了一個system_server進程。關於它的故事,咱們會在後文作專門分析,這裏先說Zygote。
當Zygote從startSystemServer返回後,將進入第四個關鍵函數:runSelectLoopMode。前面,在第一個關鍵點registerZygoteSocket中註冊了一個用於IPC的Socket,不過那時尚未地方用到它。它的用途將在這個runSelectLoopMode中體現出來,請看下面的代碼:
[-->ZygoteInit.java]
private static void runSelectLoopMode()
throws MethodAndArgsCaller {
ArrayList<FileDescriptor> fds = new ArrayList();
ArrayList<ZygoteConnection> peers = new ArrayList();
FileDescriptor[] fdArray = new FileDescriptor[4];
//sServerSocket是咱們先前在registerZygoteSocket創建的Socket
fds.add(sServerSocket.getFileDescriptor());
peers.add(null);
int loopCount = GC_LOOP_COUNT;
while (true) {
int index;
try {
fdArray = fds.toArray(fdArray);
/*
selectReadable內部調用select,使用多路複用I/O模型。
當有客戶端鏈接或有數據時,則selectReadable就會返回。
*/
index = selectReadable(fdArray);
}
else if (index == 0) {
//若有一個客戶端鏈接上,請注意客戶端在Zygote的表明是ZygoteConnection
ZygoteConnection newPeer = acceptCommandPeer();
peers.add(newPeer);
fds.add(newPeer.getFileDesciptor());
} else {
boolean done;
//客戶端發送了請求,peers.get返回的是ZygoteConnection
//後續處理將交給ZygoteConnection的runOnce函數完成。
done = peers.get(index).runOnce();
}
}
runSelectLoopMode比較簡單,就是:
· 處理客戶鏈接和客戶請求。其中客戶在Zygote中用ZygoteConnection對象來表示。
· 客戶的請求由ZygoteConnection的runOnce來處理。
建議:runSelectLoopMode比較簡單,但它使用的select的背後所表明的思想卻並不是簡單。建議讀者以此爲契機,認真學習經常使用的I/O模型,包括阻塞式、非阻塞式、多路複用、異步I/O等,掌握這些知識,對於將來編寫大型系統頗有幫助。
關於Zygote是如何處理請求的,將單獨用一節內容進行討論。
Zygote是建立Android系統中Java世界的盤古,它建立了第一個Java虛擬機,同時它又是女媧,它成功地繁殖了framework的核心system_server進程。作爲Java語言的受益者,咱們理應回顧一下Zygote建立Java世界的步驟:
· 第一天:建立AppRuntime對象,並調用它的start。此後的活動則由AppRuntime來控制。
· 次日:調用startVm建立Java虛擬機,而後調用startReg來註冊JNI函數。
· 第三天:經過JNI調用com.android.internal.os.ZygoteInit類的main函數,今後進入了Java世界。然而在這個世界剛開創的時候,什麼東西都沒有。
· 第四天:調用registerZygoteSocket。經過這個函數,它能夠響應子孫後代的請求。同時Zygote調用preloadClasses和preloadResources,爲Java世界添磚加瓦。
· 第五天:Zygote以爲本身工做壓力太大,便經過調用startSystemServer分裂一個子進程system_server來爲Java世界服務。
· 第六天:Zygote完成了Java世界的初創工做,它已經很知足了。下一步該作的就是調用runSelectLoopMode後,便沉沉地睡去了。
· 之後的日子:Zygote隨時守護在咱們的周圍,當接收到子孫後代的請求時,它會隨時醒來,爲它們工做。
若是支持中文編碼的話,我必定要爲Zygote取名爲盤古_女媧。
SystemServer的進程名實際上叫作「system_server」,這裏咱們可將其簡稱爲SS。SS作爲Zygote的嫡長子,其重要性不言而喻。關於這一點,經過代碼分析即可立刻知曉。
咱們先回顧一下SS是怎麼建立的。
String args[] = {
"--setuid=1000",
"--setgid=1000",
"--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,
3001,3002,3003",
"--capabilities=130104352,130104352",
"--runtime-init",
"--nice-name=system_server",
"com.android.server.SystemServer",
};
ZygoteConnection.Arguments parsedArgs = null;
int pid;
parsedArgs = new ZygoteConnection.Arguments(args);
intdebugFlags = parsedArgs.debugFlags;
pid = Zygote.forkSystemServer( //調用forkSystemServer
parsedArgs.uid,parsedArgs.gid,
parsedArgs.gids,debugFlags, null);
從上面的代碼中能夠看出,SS是由Zygote經過Zygote.forkSystemServer函數fork誕生出來的。這裏會有什麼玄機嗎?先來一塊兒看看forkSystemServer的實現。它是一個native函數,實如今dalvik_system_Zygote.c中,以下所示:
[-->dalvik_system_Zygote.c]
static voidDalvik_dalvik_system_Zygote_forkSystemServer(
const u4* args, JValue* pResult)
{
pid_tpid;
//根據參數,fork一個子進程
pid =forkAndSpecializeCommon(args);
if (pid > 0) {
int status;
gDvm.systemServerPid = pid;//保存system_server的進程id
//函數退出前須先檢查剛建立的子進程是否退出了。
if(waitpid(pid, &status, WNOHANG) == pid) {
//若是system_server退出了,Zygote直接幹掉了本身
//看來Zygote和SS的關係異常緊密,簡直是生死與共!
kill(getpid(), SIGKILL);
}
}
RETURN_INT(pid);
}
下面,再看看forkAndSpecializeCommon,代碼以下所示:
[-->dalvik_system_Zygote.c]
static pid_t forkAndSpecializeCommon(const u4*args)
{
pid_tpid;
uid_tuid = (uid_t) args[0];
gid_tgid = (gid_t) args[1];
ArrayObject* gids = (ArrayObject *)args[2];
u4debugFlags = args[3];
ArrayObject *rlimits = (ArrayObject *)args[4];
//設置信號處理,待會兒要看看這個函數。
setSignalHandler();
pid =fork(); //fork子進程
if (pid== 0) {
//對子進程要根據傳入的參數作一些處理,例如設置進程名,設置各類id(用戶id,組id等)
}
......
}
最後看看setSignalHandler函數,它由Zygote在fork子進程前調用,代碼以下所示:
[-->dalvik_system_Zygote.c]
static void setSignalHandler()
{
interr;
structsigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = sigchldHandler;
err =sigaction (SIGCHLD, &sa, NULL);//設置信號處理函數,該信號是子進程死亡的信號
}
//咱們直接看這個信號處理函數sigchldHandler
static void sigchldHandler(int s)
{
pid_tpid;
intstatus;
while((pid = waitpid(-1, &status, WNOHANG)) > 0) {
} else if (WIFSIGNALED(status)) {
}
}
//若是死去的子進程是SS,則Zygote把本身也幹掉了,這樣就作到了生死與共!
if(pid == gDvm.systemServerPid) {
kill(getpid(), SIGKILL);
}
}
OK,作爲Zygote的嫡長子,SS確實具備很是高的地位,居然到了與Zygote生死與共的地步!它爲何這麼重要呢?咱們如今就從forkSystemServer來分析SS究竟承擔了怎樣的工做使命。
關於源代碼定位的問題,很多人當面對浩瀚的代碼時,經常不知道具體函數是在哪一個文件中定義的。這裏,就Source insight的使用提幾點建議:
1)加入工程的時候,不要把全部目錄所有加進去,不然會致使解析速度異常緩慢。咱們能夠先加入framework目錄,如之後另有須要時,再加入其餘目錄。
2)除了Sourceinsight的工具外,還須要有一個能搜索文件中特定字符串的工具,我用的是coolfind。forkSystemServer這個函數,就是經過它在源碼中搜索到的,而且找到了實現文件dalvik_system_Zygote.c。在Linux下也有對應工具,但工做速度比coolfind緩慢。
3) 在Linux下,可經過wine(一個支持Linux平臺安裝Windows軟件的工具)安裝Source insight。
SS誕生後,便和生父Zygote分道揚鑣,它有了本身的歷史使命。它的使命是什麼呢?其代碼以下所示:
pid =Zygote.forkSystemServer();
if(pid == 0) { //SS進程返回0,那麼下面這句話就是SS的使命:
handleSystemServerProcess(parsedArgs);
}
SS調用handleSystemServerProcess來承擔本身的職責。
[-->ZygoteInit.java]
private static void handleSystemServerProcess(
ZygoteConnection.ArgumentsparsedArgs)
throws ZygoteInit.MethodAndArgsCaller {
//關閉從Zygote那裏繼承下來的Socket。
closeServerSocket();
//設置SS進程的一些參數。
setCapabilities(parsedArgs.permittedCapabilities,
parsedArgs.effectiveCapabilities);
//調用ZygoteInit函數。
RuntimeInit.zygoteInit(parsedArgs.remainingArgs);
}
好了,SS走到RuntimeInit了,它的代碼在RuntimeInit.java中,以下所示:
[-->RuntimeInit.java]
public static final void zygoteInit(String[]argv)
throws ZygoteInit.MethodAndArgsCaller {
//作一些常規初始化
commonInit();
//①native層的初始化。
zygoteInitNative();
intcurArg = 0;
for (/* curArg */ ; curArg < argv.length; curArg++) {
String arg = argv[curArg];
if (arg.equals("--")) {
curArg++;
break;
} else if (!arg.startsWith("--")) {
break;
} else if (arg.startsWith("--nice-name=")) {
String niceName = arg.substring(arg.indexOf('=') + 1);
//設置進程名爲niceName,也就是"system_server"
Process.setArgV0(niceName);
}
}
//startClass名爲"com.android.server.SystemServer"
String startClass = argv[curArg++];
String[] startArgs = new String[argv.length - curArg];
System.arraycopy(argv, curArg, startArgs, 0, startArgs.length);
//②調用startClass,也就是com.android.server.SystemServer類的main函數。
invokeStaticMain(startClass, startArgs);
}
對於上面列舉出的兩個關鍵點,咱們一個一個地分析。
先看zygoteInitNative,它是一個native函數,實如今AndroidRuntime.cpp中。
[-->AndroidRuntime.cpp]
static voidcom_android_internal_os_RuntimeInit_zygoteInit(
JNIEnv* env,jobject clazz)
{
gCurRuntime->onZygoteInit();
}
//gCurRuntime是什麼?還記得咱們在本章開始說的app_process的main函數嗎?
int main(int argc, const char* const argv[])
{
AppRuntime runtime;// 就是這個。當時咱們沒顧及它的構造函數,如今回過頭看看。
}
//AppRuntime的定義
class AppRuntime : public AndroidRuntime
static AndroidRuntime* gCurRuntime = NULL; // gCurRuntime爲全局變量。
AndroidRuntime::AndroidRuntime()
{
SkGraphics::Init();//Skia庫初始化
SkImageDecoder::SetDeviceConfig(SkBitmap::kRGB_565_Config);
SkImageRef_GlobalPool::SetRAMBudget(512 * 1024);
gCurRuntime= this; //gCurRuntime被設置爲AndroidRuntime對象本身
}
因爲SS是從Zygote fork出來的,因此它也擁有Zygote進程中定義的這個gCurRuntime,也就是AppRuntime對象。那麼,它的onZygoteInit會幹些什麼呢?它的代碼在App_main.cpp中,咱們一塊兒來看:
[-->App_main.cpp]
virtual void onZygoteInit()
{
//下面這些東西和Binder有關係,但讀者能夠先無論它。
sp<ProcessState> proc = ProcessState::self();
if(proc->supportsProcesses()) {
proc->startThreadPool();//啓動一個線程,用於Binder通訊。
}
}
一言以蔽之,SS調用zygoteInitNative後,將和Binder通訊系統創建聯繫,這樣SS就可以使用Binder了。關於Binder的知識,在第6章中將詳細介紹,讀者朋友如今沒必要關注。
再來看第二個關鍵點invokeStaticMain。代碼以下所示:
[-->RuntimeInit.java]
private static void invokeStaticMain(StringclassName, String[] argv)
throws ZygoteInit.MethodAndArgsCaller {
......//注意咱們的參數,className爲"com.android.server.SystemServer"
Class<?> cl;
try {
cl = Class.forName(className);
}catch (ClassNotFoundException ex) {
throw new RuntimeException(
"Missing class wheninvoking static main " + className,
ex);
}
Method m;
try {
//找到com.android.server.SystemServer類的main函數,確定有地方要調用它
m = cl.getMethod("main", new Class[] { String[].class });
}catch (NoSuchMethodException ex) {
......
}catch (SecurityException ex) {
......
}
int modifiers = m.getModifiers();
if(! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
......
}
//拋出一個異常,爲何不在這裏直接調用上面的main函數呢?
throw new ZygoteInit.MethodAndArgsCaller(m, argv);
}
invokeStaticMain居然拋出了一個異常,它是在哪裏被截獲呢?原來是在ZygoteInit的main函數中。請看這段代碼:
注意:咱們所在的進程是system_server。
[-->ZygoteInit.java]
....
if (argv[1].equals("true")) {
//SS進程中,拋出一個異常MethodAndArgsCaller
startSystemServer();
......
catch(MethodAndArgsCaller caller) {
//被截獲,調用caller的run函數
caller.run();
}
再來看看MethodAndArgsCaller的run函數。
public void run() {
try {
//這個mMethod爲com.android.server.SystemServer的main函數
mMethod.invoke(null, new Object[] { mArgs });
} catch(IllegalAccessException ex) {
......
}
}
拋出的這個異常最後會致使com.android.server.SystemServer類的main函數被調用。不過這裏有一個疑問,爲何不在invokeStaticMain那裏直接調用,而是採用這種拋異常的方式呢?我對這個問題的見解是:
· 這個調用是在ZygoteInit.main中,至關於Native的main函數,即入口函數,位於堆棧的頂層。若是不採用拋異常的方式,而是在invokeStaticMain那裏調用,則會浪費以前函數調用所佔用的一些調用堆棧。
關於這個問題的深層思考,讀者能夠利用fork和exec的知識。對這種拋異常的方式,我我的以爲是對exec的一種近似模擬,由於後續的工做將交給com.android.server.SystemServer類來處理。
ZygoteInit分裂產生的SS,其實就是爲了調用com.android.server.SystemServer的main函數,這簡直就是改頭換面!下面就來看看這個真實的main函數,代碼以下所示:
[-->SystemServer.java]
public static void main(String[] args) {
......
//加載libandroid_servers.so
System.loadLibrary("android_servers");
//調用native的init1函數。
init1(args);
}
其中main函數將加載libandroid_server.so庫,這個庫所包含的源碼文件在文件夾framework/base/services/jni下。
init1是native函數,在com_android_server_SystemServer.cpp中實現。來看看它,代碼以下所示:
[-->com_android_server_SystemServer.cpp]
extern "C" int system_init();
static voidandroid_server_SystemServer_init1(JNIEnv* env, jobject clazz)
{
system_init();//調用另一個函數。
}
system_init的實如今system_init.cpp中,它的代碼以下所示:
[-->system_init.cpp]
extern "C" status_t system_init()
{
//下面這些調用和Binder有關,咱們會在第6章中講述,這裏先沒必要管它。
sp<ProcessState> proc(ProcessState::self());
sp<IServiceManager> sm = defaultServiceManager();
sp<GrimReaper>grim = new GrimReaper();
sm->asBinder()->linkToDeath(grim, grim.get(), 0);
charpropBuf[PROPERTY_VALUE_MAX];
property_get("system_init.startsurfaceflinger", propBuf,"1");
if(strcmp(propBuf, "1") == 0) {
//SurfaceFlinger服務在system_server進程建立
SurfaceFlinger::instantiate();
}
......
//調用com.android.server.SystemServer類的init2函數
AndroidRuntime* runtime = AndroidRuntime::getRuntime();
runtime->callStatic("com/android/server/SystemServer","init2");
//下面這幾個函數調用和Binder通訊有關,具體內容在第6章中介紹。
if (proc->supportsProcesses()) {
ProcessState::self()->startThreadPool();
//調用joinThreadPool後,當前線程也加入到Binder通訊的大潮中
IPCThreadState::self()->joinThreadPool();
}
returnNO_ERROR;
}
init1函數建立了一些系統服務,而後把調用線程加入Binder通訊中。不過其間還經過JNI調用了com.android.server.SystemServer類的init2函數,下面就來看看這個init2函數。
init2在Java層,代碼在SystemServer.java中,以下所示:
[-->SystemServer.java]
public static final void init2() {
Threadthr = new ServerThread();
thr.setName("android.server.ServerThread");
thr.start();//啓動一個ServerThread
}
啓動了一個ServerThread線程。請直接看它的run函數。這個函數比較長,大概看看它幹了什麼便可。
[-->SystemServer.java::ServerThread的run函數]
public void run(){
....
//啓動Entropy Service
ServiceManager.addService("entropy",new EntropyService());
//啓動電源管理服務
power =new PowerManagerService();
ServiceManager.addService(Context.POWER_SERVICE, power);
//啓動電池管理服務。
battery= new BatteryService(context);
ServiceManager.addService("battery", battery);
//初始化看門狗,在拓展部分將介紹關於看門狗的知識
Watchdog.getInstance().init(context,battery, power, alarm,
ActivityManagerService.self());
//啓動WindowManager服務
wm =WindowManagerService.main(context, power,
factoryTest !=SystemServer.FACTORY_TEST_LOW_LEVEL);
ServiceManager.addService(Context.WINDOW_SERVICE,wm);
//啓動ActivityManager服務
(ActivityManagerService)ServiceManager.getService("activity"))
.setWindowManager(wm);
......//總之,系統各類重要服務都在這裏啓動
Looper.loop(); //進行消息循環,而後處理消息。關於這部份內容參見第5章。
}
init2函數比較簡單,就是單首創建一個線程,用以啓動系統各項服務,至此,讀者或許能理解SS的重要性了吧?
· Java世界的核心Service都在這裏啓動,因此它很是重要。
說明:本書不對這些Service作進一步分析,從此有機會再作作專門介紹。
SS曲折的調用流程真讓人眼花繚亂,咱們用圖4-2來展現這一過程:
圖4-2 SystemServer的調用流程
注意:init1函數最終致使進程的主線程加入到Binder通訊的大潮中,關於Binder的知識,在第6章中介紹。
前文已經講道,Zygote分裂出嫡長子system_server後,就經過runSelectLoopMode等待並處理來自客戶的消息,那麼,誰會向Zygote發送消息呢?這裏,以一個Activity的啓動爲例,具體分析Zygote是如何分裂和繁殖的。
ActivityManagerService也是由SystemServer建立的。假設經過startActivit來啓動一個新的Activity,而這個Activity附屬於一個還未啓動的進程,那麼這個進程該如何啓動呢?先來看看ActivityManagerService中的startProcessLocked函數,代碼以下所示:
[-->ActivityManagerService.java]
private final void startProcessLocked(ProcessRecordapp,
String hostingType, String hostingNameStr){
......//這個ActivityManagerService類很複雜,有14657行!!!
if("1".equals(SystemProperties.get("debug.checkjni"))) {
debugFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
}
if("1".equals(SystemProperties.get("debug.assert"))) {
debugFlags |= Zygote.DEBUG_ENABLE_ASSERT;
}
//這個Process類是Android提供的,並不是JDK中的Process類
intpid = Process.start("android.app.ActivityThread",
mSimpleProcessManagement ?app.processName : null, uid, uid,
gids, debugFlags, null);
......
}
接着來看看Process的start函數,這個Process類是android.os.Process,它的代碼在Process.java中,代碼以下所示:
[-->Process.java]
public static final int start(final StringprocessClass,final String niceName,
int uid, int gid, int[] gids,intdebugFlags,String[] zygoteArgs)
{
//注意,processClass的值是"android.app.ActivityThread"。
if(supportsProcesses()) {
try {
//調用startViaZygote。
return startViaZygote(processClass, niceName, uid, gid, gids,
debugFlags,zygoteArgs);
}
}
}
[-->Process.java::startViaZygote()]
private static int startViaZygote(final StringprocessClass,
final String niceName,final int uid, finalint gid,final int[] gids,
intdebugFlags,String[] extraArgs) throws ZygoteStartFailedEx {
int pid;
......//一些參數處理,最後調用zygoteSendArgsAndGetPid函數。
argsForZygote.add("--runtime-init");//這個參數很重要
argsForZygote.add("--setuid=" +uid);
argsForZygote.add("--setgid=" + gid);
pid =zygoteSendArgsAndGetPid(argsForZygote);
return pid;
}
[-->Process.java::zygoteSendArgsAndGetPid()]
private static intzygoteSendArgsAndGetPid(ArrayList<String> args)
throwsZygoteStartFailedEx {
intpid;
// openZygoteSocketIfNeeded?是否是打開了和Zygote通訊的Socket?
openZygoteSocketIfNeeded();
try {
//把請求的參數發到Zygote。
sZygoteWriter.write(Integer.toString(args.size()));
sZygoteWriter.newLine();
sZygoteWriter.write(arg);
sZygoteWriter.newLine();
}
//讀取Zygote處理完的結果,便得知是某個進程的pid!
sZygoteWriter.flush();
pid= sZygoteInputStream.readInt();
return pid;
}
[-->Process.java]
private static void openZygoteSocketIfNeeded()throws ZygoteStartFailedEx {
try {
sZygoteSocket = new LocalSocket();//果然如此!!
//鏈接Zygote
sZygoteSocket.connect(new LocalSocketAddress(ZYGOTE_SOCKET,
LocalSocketAddress.Namespace.RESERVED));
sZygoteInputStream
= newDataInputStream(sZygoteSocket.getInputStream());
sZygoteWriter = new BufferedWriter(
new OutputStreamWriter(
sZygoteSocket.getOutputStream()),256);
}
}
}
}
好了,ActivityManagerService終於向Zygote發送請求了。請求的參數中有一個字符串,它的值是「android.app.ActivityThread」。如今該回到Zygote處理請求那塊去看看了。
注意:因爲ActivityManagerService駐留於SystemServer進程中,因此正是SS向Zygote發送了消息。
前面有一節,題目叫「有求必應之等待請求」,那麼這一節「有求必應之響應請求」會回到ZygoteInit。下面就看看它是如何處理請求的。
[--->ZygoteInit.java]
private static void runSelectLoopMode() throwsMethodAndArgsCaller{
......
try {
fdArray = fds.toArray(fdArray);
......
else if (index == 0) {
ZygoteConnection newPeer = acceptCommandPeer();
peers.add(newPeer);
fds.add(newPeer.getFileDesciptor());
} else {
boolean done;
//調用ZygoteConnection的runOnce
done = peers.get(index).runOnce();
}
......
}
每當有請求數據發來時,Zygote都會調用ZygoteConnection的runOnce函數。ZygoteConnection代碼在ZygoteConnection.java文件中,來看看它的runOnce函數:
[-->ZygoteConnection.java]
boolean runOnce() throwsZygoteInit.MethodAndArgsCaller {
try {
args = readArgumentList();//讀取SS發送過來的參數
descriptors = mSocket.getAncillaryFileDescriptors();
}
......
int pid;
try {
parsedArgs = new Arguments(args);
applyUidSecurityPolicy(parsedArgs, peer);
//根據函數名,可知Zygote又分裂出了一個子進程。
pid =Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid,
parsedArgs.gids,parsedArgs.debugFlags, rlimits);
}
......
if(pid == 0) {
//子進程處理,這個子進程是否是咱們要建立的Activity對應的子進程呢?
handleChildProc(parsedArgs, descriptors, newStderr);
return true;
}else {
//zygote進程
return handleParentProc(pid, descriptors, parsedArgs);
}
}
接下來,看看新建立的子進程在handleChildProc中作了些什麼。
[-->ZygoteConnection.java]
private void handleChildProc(ArgumentsparsedArgs,FileDescriptor[] descriptors,
PrintStream newStderr) throwsZygoteInit.MethodAndArgsCaller {
......//根據傳入的參數設置新進程的一些屬性
//SS發來的參數中有「--runtime-init「,因此parsedArgs.runtimeInit爲true。
if(parsedArgs.runtimeInit) {
RuntimeInit.zygoteInit(parsedArgs.remainingArgs);
}else {
......
}
}
[-->RuntimeInit.java]
public static final void zygoteInit(String[]argv)
throws ZygoteInit.MethodAndArgsCaller {
//重定向標準輸出和錯誤輸出
System.setOut(new AndroidPrintStream(Log.INFO, "System.out"));
System.setErr(new AndroidPrintStream(Log.WARN, "System.err"));
commonInit();
//下面這個函數爲native函數,最終會調用AppRuntime的onZygoteInit,在那個函數中
//創建了和Binder的關係
zygoteInitNative();
int curArg = 0;
......
String startClass = argv[curArg++];
String[] startArgs = new String[argv.length - curArg];
System.arraycopy(argv, curArg, startArgs, 0, startArgs.length);
//最終仍是調用invokeStaticMain函數,這個函數咱們已經見識過了。
invokeStaticMain(startClass, startArgs);
}
Zygote分裂子進程後,本身將在handleParentProc中作一些掃尾工做,而後繼續等待請求進行下一次分裂。
這個android.app.ActivityThread類,其實是Android中apk程序所對應的進程,它的main函數就是apk程序的main函數。從這個類的命名(android.app)中也能夠看出些端倪。
經過這一節的分析,讀者能夠想到,Android系統運行的那些apk程序,其父都是zygote。這一點,能夠經過adb shell登陸後,用ps命令查看進程和父進程號來確認。
Zygote的分裂由SS控制,這個過程咱們用圖4-3來表示:
圖4-3 Zygote響應請求的過程
說明:這裏借用了UML的時序圖來表達Zygote響應請求的過程。
在分析Zygote建立虛擬機的時候,咱們說過系統默認設置的Java虛擬機堆棧最大爲16MB,這個值對於須要使用較大內存的程序(例如圖片處理程序)來講還遠遠不夠。固然,能夠修改這個默認值,例如個人HTC G7就將其修改成32MB了,可是這個改動是全局性的,也就是全部的Java程序都會是這個32MB。咱們能動態配置這個值嗎?例如:
· 設置一個配置文件,每一個進程啓動的時候根據配置文件的參數來設置堆大小。
不過正如前面所說,個人這一美好願望最終破滅了,緣由只有一個:
· Zygote是經過fork來建立子進程的,Zygote自己設置的信息會被子進程所有繼承,例如Zygote設置的堆棧爲16MB,那麼它的子進程也是用這個16MB。
關於這個問題,我目前想到了兩個解決方案:
· 爲Dalivk增長一個函數,這個函數容許動態調整最大堆的大小。
· Zygote經過fork子進程後,調用exec家族的函數來加載另一個映像,該映像對應的程序會從新建立虛擬機,從新註冊JNI函數,也就是模擬Zygote創世界中前兩天的工做,最後調用android.app.ActivityThread的main函數。這種方式應該是可行的,但難度較大,並且會影響運行速度。
關於本節所提出的問題,歡迎廣大讀者踊躍討論。
Android開機速度慢這一現象一直受人詬病,Google好像也沒有要作這方面優化的意向,那麼,在實際工做中又在哪些地方能夠作一些優化呢?根據我目前所掌握的資料分析,有三個地方耗時比較長:
· ZygoteInit的main函數中preloadClasses加載的那一千多個類。
· 開機啓動時,會對系統內全部的apk文件掃描並收集信息,這個動做耗費的時間很是長。
· SystemServer建立的那些Service,會佔用很多時間。
咱們這裏討論第一個問題,如何減小preloadClasses的時間呢?其實,這個函數是能夠去掉的,由於系統最終仍是會在使用這些類時去加載,但這樣就破壞了Android採用fork機制來建立Java進程的本意,而fork機制的好處則是顯而易見的:
· Zygote預加載的這些class,在fork子進程時,僅需作一個複製便可。這樣就節約了子進程的啓動時間。
· 根據fork的copy-on-write機制,有些類若是不作改變,甚至連複製都不用,它們會直接和父進程共享數據。這樣就會省去很多內存的佔用。
開機速度優化是一項比較複雜的研究,目前有人使用Berkeley Lab Checkpoint/Restart(BLCR)技術來提高開機速度。這一技術的構想其實挺簡單,就是對當前系統作一個快照,保存到一個文件中,當系統重啓時,直接根據文件的快照信息來恢復重啓以前的狀態。固然想法很簡單,實現倒是很複雜的,這裏,咱們對此不作進一步的討論了,讀者可自行展開深刻的思考和研究。
我在VMWare虛擬機上使用過相似的技術,它叫Snapshort。開機速度的問題我更但願Google本身能加以重視並推進它的解決。
本章咱們沒有對SystemServer作更進一步的分析,不過作爲拓展內容,這裏想介紹一下Watchdog。Watch Dog的中文意思是「看門狗」。我依稀記得,其最初存在的意義是由於早期嵌入式設備上的程序常常「跑飛」(好比說電磁干擾等),因此專門有個硬件看門狗,每隔一段時間,看門狗就去檢查一下某個參數是否是被設置了,若是發現該參數沒有被設置,則判斷爲系統出錯,而後就會強制重啓。
軟件層面上Android對SystemServer對參數是否被設置也很謹慎,專門爲它增長了一條看門狗,可它看的是哪一個門呢?對了,就是看幾個重要Service的門,一旦發現Service出了問題,就會殺掉system_server,這樣就使zygote隨其一塊兒自殺,最後致使重啓Java世界。
咱們先把SystemServe使用Watchdog的調用流程總結一下,而後以這個爲切入點來分析Watchdog。SS和Watchdog的交互流程能夠總結爲如下三個步驟:
· Watchdog. getInstance().init()
· Watchdog.getInstance().start()
· Watchdog. getInstance().addMonitor()
這三個步驟都很是簡單。先看第一步:
getInstance用於建立Watchdog,一塊兒來看看,代碼以下所示:
[-->Watchdog.java]
public static Watchdog getInstance() {
if(sWatchdog == null) {
sWatchdog= new Watchdog(); //使用了單例模式。
}
returnsWatchdog;
}
public class Watchdog extends Thread
//Watchdog從線程類派生,因此它會在單獨的一個線程中執行
private Watchdog() {
super("watchdog");
//構造一個Handler,Handler的詳細分析見第5章,讀者能夠簡單地把它看作是消息處理的地方。
//它在handleMessage函數中處理消息
mHandler = new HeartbeatHandler();
//GlobalPssCollected和內存信息有關。
mGlobalPssCollected= new GlobalPssCollected();
}
這條看門狗誕生後,再來看看init函數,代碼以下所示:
[-->Watchdog.java]
public void init(Context context, BatteryServicebattery,
PowerManagerService power, AlarmManagerService alarm,
ActivityManagerService activity) {
mResolver = context.getContentResolver();
mBattery = battery;
mPower = power;
mAlarm = alarm;
mActivity = activity;
......
mBootTime = System.currentTimeMillis();//獲得當前時間
......
}
至此,看門狗誕生的知識就介紹完了,下面咱們就讓它動起來。
SystemServer調用Watchdog的start函數,這將致使Watchdog的run在另一個線程中被執行。代碼以下所示:
[-->Watchdog.java]
public void run() {
booleanwaitedHalf = false;
while(true) {//外層while循環
mCompleted= false; //false代表各個服務的檢查還沒完成。
/*
mHandler的消息處理是在另一個線程上,這裏將給那個線程的消息隊列發條消息
請求Watchdog檢查Service是否工做正常。
*/
mHandler.sendEmptyMessage(MONITOR);
synchronized (this) {
long timeout = TIME_TO_WAIT;
long start = SystemClock.uptimeMillis();
//注意這個小while循環的條件,mForceKillSystem爲true也會致使退出循環
while (timeout > 0 && !mForceKillSystem) {
try {
wait(timeout); //等待檢查的結果
} catch(InterruptedException e) {
}
timeout = TIME_TO_WAIT -(SystemClock.uptimeMillis() - start);
}
//mCompleted爲true,表示service一切正常
if (mCompleted &&!mForceKillSystem) {
waitedHalf = false;
continue;
}
//若是mCompleted不爲true,看門狗會比較盡責,再檢查一次
if (!waitedHalf) {
......
waitedHalf = true;
continue;//再檢查一次
}
}
//已經檢查過兩次了,仍是有問題,這回是真有問題了。因此SS須要把本身幹掉。
if (!Debug.isDebuggerConnected()) {
Process.killProcess(Process.myPid());
System.exit(10); //幹掉本身
}
......
waitedHalf = false;
}
}
OK,這個run函數仍是比較簡單的,就是:
· 隔一段時間給另一個線程發送一條MONITOR消息,那個線程將檢查各個Service的健康狀況。而看門狗會等待檢查結果,若是第二次尚未返回結果,那麼它會殺掉SS。
好吧,來看看檢查線程到底是怎麼檢查Service的。
這麼多Service,哪些是看門狗比較關注的呢?一共有三個Service是須要交給Watchdog檢查的:
· ActivityManagerService
· PowerManagerService
· WindowManagerService
要想支持看門狗的檢查,就須要這些Service實現monitor接口,而後Watchdog就會調用它們的monitor函數進行檢查了。檢查的地方是在HeartbeatHandler類的handleMessage中,代碼以下所示:
[-->Watchdog.java::HeartbeatHandler]
final class HeartbeatHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
......
case MONITOR: {
......
long now =SystemClock.uptimeMillis();
final int size =mMonitors.size();
//檢查各個服務,並設置當前檢查的對象爲mCurrentMonitor
for (int i = 0 ; i <size ; i++) {
mCurrentMonitor =mMonitors.get(i);
mCurrentMonitor.monitor();//檢查這個對象
}
//若是沒問題,則設置mCompleted爲真。
synchronized (Watchdog.this){
mCompleted = true;
mCurrentMonitor = null;
}
} break;
}
}
}
那麼,Service的健康是怎麼判斷的呢?咱們以PowerManagerService爲例,先看看它是怎麼把本身交給看門狗檢查的。
[-->PowerManagerService.java]
PowerManagerService()
{
......
//在構造函數中把本身加入Watchdog的檢查隊列
Watchdog.getInstance().addMonitor(this);
}
而Watchdog調用各個monitor函數到底檢查了些什麼呢?再看看它實現的monitor函數吧。
[-->PowerManagerService.java]
public void monitor() {
//monitor原來檢查的就是這些Service是否是發生死鎖了!
synchronized (mLocks) { }
}
原來,Watchdog最怕系統服務死鎖了,對於這種狀況也只能採起殺系統的辦法了。
這種狀況,我只碰到過一次,緣由是有一個函數佔着鎖,但長時間沒有返回。沒返回的緣由是這個函數須要和硬件交互,而硬件又沒有及時返回。
關於Watchdog,咱們就介紹到這裏。另外,它還能檢查內存的使用狀況,這一部份內容讀者能夠自行研究。
本章對Zygote進程作了較爲深刻的分析,Zygote的主要工做是開創Java世界,本章介紹了它創世紀的七大步驟。另外,本章還分析了Zygote的「嫡長子」——System_server進程,這個進程是Java世界中的系統Service的駐留地,因此它很是重要。對於System_server進程,本章重點關注的是它的建立和初始化過程。此外,咱們還分析了一個Activity所屬進程的建立過程,原來這個進程是由ActivityManagerService發送請求給Zygote,最後由Zygote經過fork的方式建立的。
在本章拓展部分,咱們討論了Dalvik虛擬機對heap大小的設置及其可能的修改方法,另外還探討了Android系統開機速度的問題。最後,本章還分析了System_server中Watchdog的工做流程。