[原文出處------------Dalvik虛擬機進程和線程的創建過程分析](http://blog.csdn.net/luoshengyang/article/details/8923484)
我們知道,在Android系統中,Dalvik虛擬機是運行Linux內核之上的。如果我們把Dalvik虛擬機看作是一臺機器,那么它也有進程和線程的概念。事實上,我們的確是可以在Java代碼中創建進程和線程,也就是Dalvik虛擬機進程和線程。那么,這些Dalvik虛擬機所創建的進程和線程與其宿主Linux內核的進程和線程有什么關系呢?本文將通過Dalvik虛擬機進程和線程的創建過程來回答這個問題。
此外,從前面Dalvik虛擬機的運行過程分析一文可以知道,Dalvik虛擬機除了可以執行Java代碼之外,還可以執行Native代碼,也就是C/C++函數。這些C/C++函數在執行的過程中,又可以通過本地操作系統提供的系統調用來創建本地操作系統進程或者線程,也就是Linux進程和線程。如果在Native代碼中創建出來的進程又加載有Dalvik虛擬機,那么它實際上又可以看作是一個Dalvik虛擬機進程。另一方面,如果在Native代碼中創建出來的線程能夠執行Java代碼,那么它實際上又可以看作是一個Dalvik虛擬機線程。
這樣事情看起來似乎很復雜,因為既有Dalvik虛擬機進程和線程,又有Native操作系統進程和線程,而且它們又可以同時執行Java代碼和Native代碼。為了理清它們之間的關系,我們將按照以下四個情景來組織本文:
1. Dalvik虛擬機進程的創建過程;
2. Dalvik虛擬機線程的創建過程;
3. 只執行C/C++代碼的Native線程的創建過程;
4. 能同時執行C/C++代碼和Java代碼的Native線程的創建過程。
對于上述進程和線程,Android系統都分別提供有接口來創建:
1. Dalvik虛擬機進程可以通過android.os.Process類的靜態成員函數start來創建;
2. Dalvik虛擬機線程可以通過java.lang.Thread類的成員函數start來創建;
3. 只執行C/C++代碼的Native線程可以通過C++類Thread的成員函數run來創建;
4. 能同時執行C/C++代碼和Java代碼的Native線程也可以通過C++類Thread的成員函數run來創建;
接下來,我們就按照上述四個情況來分析Dalvik虛擬機進程和線程和Native操作系統進程和線程的關系。
一. Dalvik虛擬機進程的創建過程
Dalvik虛擬機進程實際上就是通常我們所說的Android應用程序進程。從前面[Android應用程序進程啟動過程的源代碼分析](http://blog.csdn.net/luoshengyang/article/details/6747696)一文可以知道,Android應用程序進程是由ActivityManagerService服務通過android.os.Process類的靜態成員函數start來請求Zygote進程創建的,而Zyogte進程最終又是通過dalvik.system.Zygote類的靜態成員函數forkAndSpecialize來創建該Android應用程序進程的。因此,接下來我們就從dalvik.system.Zygote類的靜態成員函數forkAndSpecialize開始分析Dalvik虛擬機進程的創建過程,如圖1所示:

圖1 Dalvik虛擬機進程的創建過程
這個過程可以分為3個步驟,接下來我們就詳細分析每一個步驟。
**Step 1. Zygote.forkAndSpecialize**
~~~
public class Zygote {
......
native public static int forkAndSpecialize(int uid, int gid, int[] gids,
int debugFlags, int[][] rlimits);
......
}
~~~
這個函數定義在文件libcore/dalvik/src/main/java/dalvik/system/Zygote.java中。
Zygote類的靜態成員函數forkAndSpecialize是一個JNI方法,它是由C++層的函數Dalvik_dalvik_system_Zygote_forkAndSpecialize來實現的,如下所示:
~~~
/* native public static int forkAndSpecialize(int uid, int gid,
* int[] gids, int debugFlags);
*/
static void Dalvik_dalvik_system_Zygote_forkAndSpecialize(const u4* args,
JValue* pResult)
{
pid_t pid;
pid = forkAndSpecializeCommon(args, false);
RETURN_INT(pid);
}
~~~
這個函數定義在文件dalvik/vm/native/dalvik_system_Zygote.c中。
注意,參數args指向的是一個u4數組,它里面包含了所有從Java層傳遞進來的參數,這是由Dalvik虛擬機封裝的。另外一個參數pResult用來保存JNI方法調用結果,這是通過宏RETURN_INT來實現的。
函數Dalvik_dalvik_system_Zygote_forkAndSpecialize的實現很簡單,它通過調用另外一個函數forkAndSpecializeCommon來創建一個Dalvik虛擬機進程。
**Step 2. forkAndSpecializeCommon**
~~~
/*
* Utility routine to fork zygote and specialize the child process.
*/
static pid_t forkAndSpecializeCommon(const u4* args, bool isSystemServer)
{
pid_t pid;
uid_t uid = (uid_t) args[0];
gid_t gid = (gid_t) args[1];
ArrayObject* gids = (ArrayObject *)args[2];
u4 debugFlags = args[3];
ArrayObject *rlimits = (ArrayObject *)args[4];
int64_t permittedCapabilities, effectiveCapabilities;
if (isSystemServer) {
/*
* Don't use GET_ARG_LONG here for now. gcc is generating code
* that uses register d8 as a temporary, and that's coming out
* scrambled in the child process. b/3138621
*/
//permittedCapabilities = GET_ARG_LONG(args, 5);
//effectiveCapabilities = GET_ARG_LONG(args, 7);
permittedCapabilities = args[5] | (int64_t) args[6] << 32;
effectiveCapabilities = args[7] | (int64_t) args[8] << 32;
} else {
permittedCapabilities = effectiveCapabilities = 0;
}
if (!gDvm.zygote) {
......
return -1;
}
......
pid = fork();
if (pid == 0) {
int err;
......
err = setgroupsIntarray(gids);
......
err = setrlimitsFromArray(rlimits);
......
err = setgid(gid);
......
err = setuid(uid);
......
err = setCapabilities(permittedCapabilities, effectiveCapabilities);
......
enableDebugFeatures(debugFlags);
......
gDvm.zygote = false;
if (!dvmInitAfterZygote()) {
......
dvmAbort();
}
} else if (pid > 0) {
/* the parent process */
}
return pid;
}
~~~
這個函數定義在文件dalvik/vm/native/dalvik_system_Zygote.c中。
函數forkAndSpecializeCommon除了可以用來創建普通的Android應用程序進程之外,還用來創建System進程。Android系統中的System進程和普通的Android應用程序進程一樣,也是由Zygote進程負責創建的,具體可以參考前面[Android系統進程Zygote啟動過程的源代碼分析](http://blog.csdn.net/luoshengyang/article/details/6768304)一文。
當函數forkAndSpecializeCommon是調用來創建System進程的時候,參數isSystemServer的值就等于true,這時候在參數列表args就會包含兩個額外的參數permittedCapabilities和effectiveCapabilities。其中,permittedCapabilities表示System進程允許的特權,而effectiveCapabilities表示System進程當前的有效特權,這兩個參數的關系就類似于進程的uid和euid的關系一樣。
進程的特權是什么概念呢?從Linux 2.2開始,Root用戶的權限被劃分成了一系列的子權限,每一個子權限都通過一個bit來表示,這些bit的定義可以參考[CAPABILITIES(7)](http://man7.org/linux/man-pages/man7/capabilities.7.html)。每一個進程都關聯有一個u32整數,用來描述它所具有的Root用戶子權限,我們可以通過系統調用capset來進行設置。
參考前面[Android系統進程Zygote啟動過程的源代碼分析](http://blog.csdn.net/luoshengyang/article/details/6768304)一篇文章可以知道,Zygote進程在創建System進程的時候,給它指定的permittedCapabilities和effectiveCapabilities均為130104352,因此,System進程在運行的時候,就可以獲得一些Root用戶特權。
當參數isSystemServer的值等于false的時候,變量permittedCapabilities和effectiveCapabilities的值被設置為0,也就是說,由Zygote進程創建出來的Android應用程序進程是不具有任何的Root用戶特權的。
除了上述的permittedCapabilities和effectiveCapabilities之外,參數列表args還包含了其它的參數:
- uid:要創建的進程的用戶ID。一般來說,每一個應用程序進程都有一個唯一的用戶ID,用來將應用程序進程封閉在一個沙箱里面運行。
- gid:要創建的進程的用戶組ID。一般來說,每一個應用程序進程都有一個唯一的用戶組ID,也是用來將應用程序進程封閉在一個沙箱里面運行。
- gids:要創建的進程的額外用戶組ID。這個額外的用戶組ID實際上對應的就是應用程序所申請的資源訪權限。
- debugFlags:要創建的進程在運行時的調試選項。例如,我們可以將debugFlags的DEBUG_ENABLE_CHECKJNI位設置為1,從而打開該進程中的Dalvik虛擬機的JNI檢查選項。
- rlimits:要創建的進程所受到的資源限制。例如,該進程所能打開文件的個數。
了解上述參數的含義之后,函數forkAndSpecializeCommon的實現就容易理解了,它主要就是調用系統調用fork來創建一個進程。我們知道系統調用fork執行完成之后,會有兩次返回,其中一次是返回當前進程中,另外一次是返回到新創建的進程中。當系統調用fork返回到新創建的進程的時候,它的返回值pid就會等于0,這時候就可以調用相應的函數來設置新創建的進程的uid、gid、gids、debugFlags和rlimits,從而將限制了新創建的進程的權限。
對于函數forkAndSpecializeCommon的實現,還有兩個地方是需要注意的。
* 第一個地方是只有Zygote進程才有權限創建System進程和Android應用程序進程。從前面[Dalvik虛擬機的啟動過程分析](http://blog.csdn.net/luoshengyang/article/details/8885792)一文可以知道,Zygote進程在啟動運行在它里面的Dalvik虛擬機的時候,gDvm.zygote的值會等于true,這時候函數forkAndSpecializeCommon才可以使用系統調用fork來創建一個新的進程。
* 第二個地方是在新創建出來的進程中,gDvm.zygote的值會被設置為false,以表示它不是Zygote進程。我們知道,當一個進程使用系統調用fork來創建一個新進程的時候,前者就稱為父進程,后者就稱為子進程。這時候父進程和子進程共享的地址空間是一樣的,但是只要某個地址被父進程或者子進程進行寫入操作的時候,這塊被寫入的地址空間才會在父進程和子進程之間獨立開來,這種機制就稱為COW(copy on write)。因此,當函數forkAndSpecializeCommon將新創建的進程中的gDvm.zygote的值設置為false的時候, Zygote進程的gDvm.zygote的值仍然保持為true。
從上述的第二點的描述還可以進一步看出,由Zygote進程創建出來的System進程和Android應用程序進程實際上是共享了很多東西,而且只要這些東西都是只讀的時候,它們就會一直被共享著。從前面[Dalvik虛擬機的啟動過程分析](http://blog.csdn.net/luoshengyang/article/details/8885792)一文可以知道,Zygote進程在啟動的過程中,加載了很多東西,例如,Java和Android核心類庫(dex文件)及其JNI方法(so文件)。這些dex文件和so文件的只讀段,例如代碼段,都會一直在Zygote進程、System進程和Android應用程序進程中進行共享。這樣,我們在Zygote進程中進行的大量預加載行為就獲得了價值,一方面是可以加快System進程和Android應用程序進程的啟動過程中,另外一方面也使得系統的整體內存消耗減少。
此外,運行在Zygote進程中的Dalvik虛擬機開始的時候也會與System進程和Android應用程序進程一起共享,但是由于上述的COW機制,在必要的時候,System進程和Android應用程序進程還是會復制一份出來的,從而使得它們都具有獨立的Dalvik虛擬機實例。
最后,函數forkAndSpecializeCommon還會調用函數dvmInitAfterZygote來進一步對在新創建的進程中運行的Dalvik虛擬機進行初始化,接下來我們就繼續分析它的實現。
**Step 3. dvmInitAfterZygote**
~~~
/*
* Do non-zygote-mode initialization. This is done during VM init for
* standard startup, or after a "zygote fork" when creating a new process.
*/
bool dvmInitAfterZygote(void)
{
......
/*
* Post-zygote heap initialization, including starting
* the HeapWorker thread.
*/
if (!dvmGcStartupAfterZygote())
return false;
......
/* start signal catcher thread that dumps stacks on SIGQUIT */
if (!gDvm.reduceSignals && !gDvm.noQuitHandler) {
if (!dvmSignalCatcherStartup())
return false;
}
/* start stdout/stderr copier, if requested */
if (gDvm.logStdio) {
if (!dvmStdioConverterStartup())
return false;
}
......
/*
* Start JDWP thread. If the command-line debugger flags specified
* "suspend=y", this will pause the VM. We probably want this to
* come last.
*/
if (!dvmInitJDWP()) {
LOGD("JDWP init failed; continuing anyway\n");
}
......
#ifdef WITH_JIT
if (gDvm.executionMode == kExecutionModeJit) {
if (!dvmCompilerStartup())
return false;
}
#endif
return true;
}
~~~
這個函數定義在文件dalvik/vm/Init.c中。
函數dvmInitAfterZygote執行的Dalvik虛擬機初始化操作包括:
1. 調用函數dvmGcStartupAfterZygote來進行一次GC。
2. 調用函數dvmSignalCatcherStartup來啟動一個Linux信號收集線程,主要是用來捕捉SIGQUIT信號,以便可以在進程退出前將各個線程的堆棧DUMP出來。
3. 調用函數dvmStdioConverterStartup來啟動一個標準輸出重定向線程,該線程負責將當前進程的標準輸出(stdout和stderr)重定向到日志輸出系統中去,前提是設置了Dalvik虛擬機的啟動選項-Xlog-stdio。
4. 調用函數dvmInitJDWP來啟動一個JDWP線程,以便我們可以用DDMS工具來調試進程中的Dalvik虛擬機。
5. 調用函數dvmCompilerStartup來啟動JIT,前提是當前使用的Dalvik虛擬機在編譯時支持JIT,并且該Dalvik虛擬機在啟動時指定了-Xint:jit選項。
這一步執先完成之后,一個Dalvik虛擬機進程就創建完成了,從中我們就可以得出結論:一個Dalvik虛擬機進程實際上就是一個Linux進程。
二. Dalvik虛擬機線程的創建過程
在Java代碼中,我們可以通過java.lang.Thread類的成員函數start來創建一個Dalvik虛擬機線程,因此,接下來我們就從這個函數開始分析Dalvik虛擬機線程的創建過程,如圖2所示:

圖2 Dalvik虛擬機線程的創建過程
這個過程可以分為10個步驟,接下來我們就詳細分析每一個步驟。
**Step 1. Thread.start**
~~~
public class Thread implements Runnable {
......
public synchronized void start() {
if (hasBeenStarted) {
throw new IllegalThreadStateException("Thread already started."); // TODO Externalize?
}
hasBeenStarted = true;
VMThread.create(this, stackSize);
}
......
}
~~~
這個函數定義在文件libcore/luni/src/main/java/java/lang/Thread.java中。
Thread類的成員函數start首先檢查成員變量hasBeenStarted的值是否等于true。如果等于true的話,那么就說明當前正在處理的Thread對象所描述的Java線程已經啟動起來了。一個Java線程是不能重復啟動的,否則的話,Thread類的成員函數start就會拋出一個類型為IllegalThreadStateException的異常。
通過了上面的檢查之后,Thread類的成員函數start接下來就繼續調用VMThread類的靜態成員函數create來創建一個線程。
**Step 2. VMThread.create**
~~~
class VMThread
{
......
native static void create(Thread t, long stacksize);
......
}
~~~
這個函數定義在文件luni/src/main/java/java/lang/VMThread.java中。
VMThread類的靜態成員函數create是一個JNI方法,它是C++層的函數Dalvik_java_lang_VMThread_create來實現的,如下所示:
~~~
/*
* static void create(Thread t, long stacksize)
*
* This is eventually called as a result of Thread.start().
*
* Throws an exception on failure.
*/
static void Dalvik_java_lang_VMThread_create(const u4* args, JValue* pResult)
{
Object* threadObj = (Object*) args[0];
s8 stackSize = GET_ARG_LONG(args, 1);
/* copying collector will pin threadObj for us since it was an argument */
dvmCreateInterpThread(threadObj, (int) stackSize);
RETURN_VOID();
}
~~~
這個函數定義在文件dalvik/vm/native/java_lang_VMThread.c中。
函數Dalvik_java_lang_VMThread_create的實現很簡單,它將Java層傳遞過來的參數獲取出來之后,就調用另外一個函數dvmCreateInterpThread來執行創建線程的工作。
**Step 3. dvmCreateInterpThread**
~~~
bool dvmCreateInterpThread(Object* threadObj, int reqStackSize)
{
pthread_attr_t threadAttr;
pthread_t threadHandle;
......
Thread* newThread = NULL;
......
int stackSize;
......
if (reqStackSize == 0)
stackSize = gDvm.stackSize;
else if (reqStackSize < kMinStackSize)
stackSize = kMinStackSize;
else if (reqStackSize > kMaxStackSize)
stackSize = kMaxStackSize;
else
stackSize = reqStackSize;
pthread_attr_init(&threadAttr);
pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_DETACHED);
......
newThread = allocThread(stackSize);
......
newThread->threadObj = threadObj;
......
int cc = pthread_create(&threadHandle, &threadAttr, interpThreadStart,
newThread);
......
while (newThread->status != THREAD_STARTING)
pthread_cond_wait(&gDvm.threadStartCond, &gDvm.threadListLock);
......
newThread->next = gDvm.threadList->next;
if (newThread->next != NULL)
newThread->next->prev = newThread;
newThread->prev = gDvm.threadList;
gDvm.threadList->next = newThread;
......
newThread->status = THREAD_VMWAIT;
pthread_cond_broadcast(&gDvm.threadStartCond);
......
return true;
}
~~~
這個函數定義在文件dalvik/vm/Thread.c中。
參數reqStackSize表示要創建的Dalvik虛擬機線程的Java棧大小。Dalvik虛擬機線程實際上具有兩個棧,一個是Java棧,另一個是Native棧。Native棧是在調用Native代碼時使用的,它是由操作系統來管理的,而Java棧是由Dalvik虛擬機來管理的。從前面[Dalvik虛擬機的運行過程分析](http://blog.csdn.net/luoshengyang/article/details/8914953)一文可以知道,Dalvik虛擬機解釋器在執行Java代碼,每當遇到函數調用指令,就會在當前線程的Java棧上創建一個幀,用來保存當前函數的執行狀態,以便要調用的函數執行完成后可以返回到當前函數來。
當參數reqStackSize的值等于0的時候,那么就會使用保存在gDvm.stackSize中的值作為接下來要創建的線程的Java棧大小。我們可以通過Dalvik虛擬機的啟動選項-Xss來指定gDvm.stackSize的值。如果沒有指定,那么gDvm.stackSize的值就設置為kDefaultStackSize (12*1024) 個字節。
另外,如果參數reqStackSize的值不等于0,那么它必須大于等于kMinStackSize(512+768),并且小于等于kMaxStackSize(256*1024+768),否則的話,它就會被修正。
參數threadObj描述的是Java層的一個Thread對象,它在Dalvik虛擬機中對應有一個Native層的Thread對象。這個Native層的Thread對象是通函數allocThread來分配的,并且與它對應的Java層的Thread對象會保存在它的成員變量threadObj中。
函數dvmCreateInterpThread是通過函數pthread_create來創建一個線程。函數pthread_create在創建一個線程的時候,需要知道該線程的屬性。這些屬性是通過一個pthread_attr_t結構體來描述的,而這個pthread_attr_t結構體可以調用函數pthread_attr_init來初始化,以及調用函數pthread_attr_setdetachstate來設置它的分離狀態。
函數pthread_create實際上是由pthread庫提供的一個函數,它最終是通過系統調用clone來請求內核創建一個線程的。由此就可以看出,Dalvik虛擬機線程實際上就是本地操作系統線程。
新創建的Dalvik虛擬機線程啟動完成之后,就會將自己的狀態設置為THREAD_STARTING,而當前線程會通過一個while循環來等待新創建的Dalvik虛擬機線程的狀態被設置為THREAD_STARTING之后再繼續往前執行,主要就是:
1. 將用來描述新創建的Dalvik虛擬機線程的Native層的Thread對象保存在gDvm.threadList所描述的一個線程列表中,這是因為當前所有Dalvik虛擬機線程都保存在這個列表中。
2. 將新創建的Dalvik虛擬機線程的狀態設置為THREAD_VMWAIT,使得新創建的Dalvik虛擬機線程繼續往前執行,這是因為新創建的Dalvik虛擬機線程將自己的狀態設置為THREAD_STARTING喚醒創建它的線程之后,又會等待創建它的線程通知它繼續往前執行。
注意,前面在調用函數pthread_create來創建新的Dalvik虛擬機線程時,指定該Dalvik虛擬機線程的入口點函數為interpThreadStart,因此,接下來我們繼續分析函數interpThreadStart的實現,以便可以了解新創建的Dalvik虛擬機線程的啟動過程。
**Step 4. interpThreadStart**
~~~
static void* interpThreadStart(void* arg)
{
Thread* self = (Thread*) arg;
......
prepareThread(self);
......
self->status = THREAD_STARTING;
pthread_cond_broadcast(&gDvm.threadStartCond);
......
while (self->status != THREAD_VMWAIT)
pthread_cond_wait(&gDvm.threadStartCond, &gDvm.threadListLock);
......
self->jniEnv = dvmCreateJNIEnv(self);
......
dvmChangeStatus(self, THREAD_RUNNING);
......
if (gDvm.debuggerConnected)
dvmDbgPostThreadStart(self);
......
int priority = dvmGetFieldInt(self->threadObj,
gDvm.offJavaLangThread_priority);
dvmChangeThreadPriority(self, priority);
......
Method* run = self->threadObj->clazz->vtable[gDvm.voffJavaLangThread_run];
JValue unused;
......
dvmCallMethod(self, run, self->threadObj, &unused);
......
dvmDetachCurrentThread();
return NULL;
}
~~~
這個函數定義在文件dalvik/vm/Thread.c中。
參數arg指向的是一個Native層的Thread對象,這個Thread對象最后會被保存在變量self,用來描述新創建的Dalvik虛擬機線程,也就是當前執行的線程。
函數interpThreadStart一方面是對新創建的Dalvik虛擬機線程進行初始化,另一方面是執行新創建的Dalvik虛擬機線程的主體函數,這些工作包括:
1. 調用函數prepareThread來初始化新創建的Dalvik虛擬機線程。
2. 將新創建的Dalvik虛擬機線程的狀態設置為THREAD_STARTING,以便其父線程,也就是創建它的線程可以繼續往前執行。
3. 通過一個while循環來等待父線程通知自己繼續往前執行,也就是等待父線程將自己的狀態設置為THREAD_VMWAIT。
4. 調用函數dvmCreateJNIEnv來為新創建的Dalvik虛擬機線程創建一個JNI環境。
5. 調用函數dvmChangeStatus將新創建的Dalvik虛擬機線程的狀態設置為THREAD_RUNNING,表示它正式進入運行狀態。
6. 如果此時gDvm.debuggerConnected的值等于true,那么就說明有調試器連接到當前Dalvik虛擬機來了,這時候就調用函數dvmDbgPostThreadStart來通知調試器新創建了一個線程。
7. 調用函數dvmChangeThreadPriority來設置新創建的Dalvik虛擬機線程的優先級,這個優先級值保存在用來描述新創建的Dalvik虛擬機線程的一個Java層Thread對象的成員變量priority中。
8. 找到Java層的java.lang.Thread類的成員函數run,并且通過函數dvmCallMethod來交給Dalvik虛擬機解釋器執行,這個java.lang.Thread類的成員函數run即為Dalvik虛擬機線程的Java代碼入口點函數。
9. 從函數dvmCallMethod返回來之后,新創建的Dalvik虛擬機線程就完成自己的使命了,這時候就可以調用函數dvmDetachCurrentThread來執行清理工作。
接下來,我們主要分析第1、4、8和第9個操作,也就是函數prepareThread、dvmCreateJNIEnv、dvmCallMethod和dvmDetachCurrentThread的實現,以便可以了解一個新創建的Dalvik虛擬機線程的初始化、運行和退出過程。
**Step 5. prepareThread**
~~~
static bool prepareThread(Thread* thread)
{
......
/*
* Initialize our reference tracking tables.
*
* Most threads won't use jniMonitorRefTable, so we clear out the
* structure but don't call the init function (which allocs storage).
*/
#ifdef USE_INDIRECT_REF
if (!dvmInitIndirectRefTable(&thread->jniLocalRefTable,
kJniLocalRefMin, kJniLocalRefMax, kIndirectKindLocal))
return false;
#else
/*
* The JNI local ref table *must* be fixed-size because we keep pointers
* into the table in our stack frames.
*/
if (!dvmInitReferenceTable(&thread->jniLocalRefTable,
kJniLocalRefMax, kJniLocalRefMax))
return false;
#endif
if (!dvmInitReferenceTable(&thread->internalLocalRefTable,
kInternalRefDefault, kInternalRefMax))
return false;
memset(&thread->jniMonitorRefTable, 0, sizeof(thread->jniMonitorRefTable));
......
return true;
}
~~~
這個函數定義在文件dalvik/vm/Thread.c中。
函數prepareThread最主要的就是對參數thread所描述的一個Dalvik虛擬機線程的三個引用表進行初始化。
第一個是JNI本地引用表。一個Dalvik虛擬機線程在執行JNI方法的時候,可能會需要訪問Java層的對象。這些Java層對象在被JNI方法訪問之前,需要往當前Dalvik虛擬機線程的JNI方法本地引用表添加一個引用,以便它們不會被GC回收。JNI方法本地引用表有兩種實現方式,一種是添加到它里面的是間接引用,另一種是直接引用。兩者的區別在于,在進行GC的時候,間接引用的Java對象可以移動,而直接引用的Java對象不可以移動。在編譯Dalvik虛擬機的時候,可以通過宏USE_INDIRECT_REF來決定使用直接引用表,還是間接引用表。
第二個是Dalvik虛擬機內部引用表。有時候,我們需要在Dalvik虛擬機內部為線程創建一些對象,這些對象需要添加到一個Dalvik虛擬機內部引用表中去,以便在線程退出時,可以對它們進行清理。
第三個是Monitor引用表。一個Dalvik虛擬機線程在執行JNI方法的時候,除了可能需要訪問Java層的對象之外,還可能需要進行一些同步操作,也就是進行MonitorEnter和MonitorExit操作。注意,MonitorEnter和MonitorExit是Java代碼中的同步指令,用來實現synchronized代碼塊或者函數的。Dalvik虛擬機需要跟蹤在JNI方法中所執行的MonitorEnter操作,也就是將被Monitor的對象添加到Monitor引用表中去,以便在JNI方法退出時,可以隱式地進行MonitorExit操作,避免出現不對稱的MonitorEnter和MonitorExit操作。
注意,Monitor引用表實際沒有被執行初始化, 函數prepareThread只是對它進行清0,這是因為我們一般很少在JNI方法中執行同步操作的,因此,就最好在使用的時候再進行初始化。
這一步執行完成之后,回到前面的Step 4中,即函數interpThreadStart中,接下來它就會調用另外一個函數dvmCreateJNIEnv來為新創建的Dalvik虛擬機線程創建一個JNI環境。
**Step 6. dvmCreateJNIEnv**
函數定義在文件dalvik/vm/Jni.c中,它的實現可以參考前面[Dalvik虛擬機的啟動過程分析](http://blog.csdn.net/luoshengyang/article/details/8885792)一文,主要就是用來為新創建的Dalvik虛擬機線程創建一個JNI環境,也就是創建一個JNIEnvExt對象,并且為該JNIEnvExt對象創建一個Java代碼訪問函數表。有了這個Java代碼訪問函數表之后,我們才可以在JNI方法中訪問Java對象,以及執行Java代碼。
這一步執行完成之后,回到前面的Step 4中,即函數interpThreadStart中,接下來它就會調用另外一個函數dvmCallMethod來通知Dalvik虛擬機解釋器執行java.lang.Thread類的成員函數run。
**Step 7. dvmCallMethod**
~~~
void dvmCallMethod(Thread* self, const Method* method, Object* obj,
JValue* pResult, ...)
{
va_list args;
va_start(args, pResult);
dvmCallMethodV(self, method, obj, false, pResult, args);
va_end(args);
}
~~~
這個函數定義在文件dalvik/vm/interp/Stack.c中。
函數dvmCallMethod的實現很簡單,它通過調用另外一個函數dvmCallMethodV來通知Dalvik虛擬機解釋器執行參數method所描述的一個Java函數。在我們這個場景中,實際上就是執行java.lang.Thread類的成員函數run。
**Step 8. dvmCallMethodV**
這個函數定義在文件dalvik/vm/interp/Stack.c中。在前面[Dalvik虛擬機的運行過程分析](http://blog.csdn.net/luoshengyang/article/details/8914953)一文中,我們已經分析過函數dvmCallMethodV的實現了,它主要就是調用另外一個函數dvmInterpret來啟動Dalvik虛擬機解釋器,并且解釋執行指定的Java代碼。
如前所述,在我們這個場景中,接下來要執行的Java代碼便是java.lang.Thread類的成員函數run。接下來,我們就繼續分析java.lang.Thread類的成員函數run的實現,以便可以了解Dalvik虛擬機線程的Java代碼入口點。
**Step 9. Thread.run**
~~~
public class Thread implements Runnable {
......
Runnable target;
......
public void run() {
if (target != null) {
target.run();
}
}
......
}
~~~
這個函數定義在文件libcore/luni/src/main/java/java/lang/Thread.java中。
Thread類的成員函數run首先檢查成員變量target的值。如果不等于null的話,那么就會調用它所指向的一個Runnable對象的成員函數run來作為新創建的Dalvik虛擬機線程的執行主體,否則的話,就什么也不做。
一般來說,我們都是通過從Thread繼承下來一個子類來創建一個Dalvik虛擬機線程的。在繼承下來的Thread子類中,重寫父類Thread的成員函數run,這時候這一步所執行的函數就是子類的成員函數run了。
這一步執行完成之后,回到前面的Step 4中,即函數interpThreadStart中,這時候新創建的Dalvik虛擬機線程就執行完成主體函數了,因此,接下來它就即將要退出。不過在退出之前,函數interpThreadStart會調用另外一個函數dvmDetachCurrentThread來執行清理工作。
接下來,我們就繼續分析函數dvmDetachCurrentThread的實現。
**Step 10. dvmDetachCurrentThread**
~~~
void dvmDetachCurrentThread(void)
{
Thread* self = dvmThreadSelf();
......
/*
* Release any held monitors. Since there are no interpreted stack
* frames, the only thing left are the monitors held by JNI MonitorEnter
* calls.
*/
dvmReleaseJniMonitors(self);
......
/*
* Do some thread-exit uncaught exception processing if necessary.
*/
if (dvmCheckException(self))
threadExitUncaughtException(self, group);
......
vmThread = dvmGetFieldObject(self->threadObj,
gDvm.offJavaLangThread_vmThread);
......
/*
* Tell the debugger & DDM. This may cause the current thread or all
* threads to suspend.
*
* The JDWP spec is somewhat vague about when this happens, other than
* that it's issued by the dying thread, which may still appear in
* an "all threads" listing.
*/
if (gDvm.debuggerConnected)
dvmDbgPostThreadDeath(self);
......
dvmObjectNotifyAll(self, vmThread);
......
/*
* Lose the JNI context.
*/
dvmDestroyJNIEnv(self->jniEnv);
self->jniEnv = NULL;
self->status = THREAD_ZOMBIE;
/*
* Remove ourselves from the internal thread list.
*/
unlinkThread(self);
......
freeThread(self);
}
~~~
這個函數定義在文件dalvik/vm/Thread.c中。
函數dvmDetachCurrentThread首先是調用函數dvmThreadSelf來獲得用來描述當前即將要退出的Dalvik虛擬機線程的Native層的Thread對象self。有了這個Native層的Thread對象self之后,就可以開始執行相應的清理工作了。這些清理工作主要包括:
1. 調用函數dvmReleaseJniMonitors來釋放那些在JNI方法中持有的Monitor,也就是MonitorExit那些被MonitorEnter了的對象。
2. 調用函數dvmCheckException來檢查線程是否有未處理異常。如果有的話,那么就調用函數threadExitUncaughtException將它們交給thread-exit-uncaught-exception handler處理。
3. 檢查當前Dalvik虛擬機是否被調試器連接了,即檢查gDvm.debuggerConnected的值是否等于true。如果被調試器連接了的話,那么就調用函數dvmDbgPostThreadDeath通知調試器當前線程要退出了。
4. 調用函數dvmObjectNotifyAll來向那些調用了Thread.join等待當前線程結束的線程發送通知。
5. 調用函數dvmDestroyJNIEnv來銷毀用來描述當前JNI上下文環境的一個JNIEnvExt對象。
6. 將當前線程的狀態設置為僵尸狀態(THREAD_ZOMBIE),并且調用函數unlinkThread將當前線程從Dalvik虛擬機的線程列表中移除。
7. 調用函數freeThread來釋放當前線程的Java棧和各個引用表(即前面Step 5所描述的三個引用表)所占用的內存,以及釋放Thread對象self所占用的內存。
這一步執行完成之后,回到前面的Step 4中,即函數interpThreadStart中,這時候新創建的Dalvik虛擬機線程就結束了,整個Dalvik虛擬機線程的創建、運行和結束過程也分析完成了,從中我們就可以得出結論:一個Dalvik虛擬機線程實際上就是一個Linux線程。
三. 只執行C/C++代碼的Native線程的創建過程
我們這里所說的Native線程就是指本地操作系統線程,它們是Dalvik虛擬機在執行C/C++代碼的過程中創建的,很顯然它們就是Linux線程。例如,在C/C++代碼中,我們可以通過C++類Thread的成員函數run來創建一個Native線程。接下來我們就從C++類Thread的成員函數run開始分析Dalvik虛擬機中的Native線程的創建過程,如圖3所示:

圖3 只執行C/C++代碼的Native線程的創建過程
這個過程可以分為3個步驟,接下來我們就詳細分析每一個步驟。
**Step 1. Thread.run**
~~~
status_t Thread::run(const char* name, int32_t priority, size_t stack)
{
Mutex::Autolock _l(mLock);
......
bool res;
if (mCanCallJava) {
res = createThreadEtc(_threadLoop,
this, name, priority, stack, &mThread);
} else {
res = androidCreateRawThreadEtc(_threadLoop,
this, name, priority, stack, &mThread);
}
......
return NO_ERROR;
}
~~~
這個函數定義在文件frameworks/base/libs/utils/Threads.cpp中。
我們在創建一個Thread對象的時候,可以指定它所描述的Native線程是否可以調用Java代碼,它的構造函數原型如下所示:
~~~
class Thread : virtual public RefBase
{
public:
......
Thread(bool canCallJava = true);
......
};
~~~
這個函數定義在文件frameworks/base/include/utils/threads.h中。
回到Thread類的成員函數run中,如果當前正在處理的Thread對象所描述的Native線程可以調用Java代碼,那么它的成員變量mCanCallJava的值就等于false,這時候就會調用另外一個函數androidCreateRawThreadEtc來創建這個Native線程。
**Step 2. androidCreateRawThreadEtc**
~~~
int androidCreateRawThreadEtc(android_thread_func_t entryFunction,
void *userData,
const char* threadName,
int32_t threadPriority,
size_t threadStackSize,
android_thread_id_t *threadId)
{
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
......
if (threadStackSize) {
pthread_attr_setstacksize(&attr, threadStackSize);
}
errno = 0;
pthread_t thread;
int result = pthread_create(&thread, &attr,
(android_pthread_entry)entryFunction, userData);
......
if (threadId != NULL) {
*threadId = (android_thread_id_t)thread; // XXX: this is not portable
}
return 1;
}
~~~
這個函數定義在文件frameworks/base/libs/utils/Threads.cpp中。
從函數androidCreateRawThreadEtc的定義就可以看出,在Android系統中,Native線程和Dalvik虛擬機線程一樣,都是通過pthread庫提供的函數pthread_create來創建的,其中,參數entryFunction指向的就是新創建的Native線程的入口點。
參數entryFunction是從前面的Step 1傳進來的,它指向的是Thread類的靜態成員函數_threadLoop,接下來我們就繼續分析它的實現,以便可以了解一個Native線程的運行過程。
**Step 3. Thread._threadLoop**
~~~
int Thread::_threadLoop(void* user)
{
Thread* const self = static_cast<Thread*>(user);
sp<Thread> strong(self->mHoldSelf);
wp<Thread> weak(strong);
......
bool first = true;
do {
bool result;
if (first) {
first = false;
self->mStatus = self->readyToRun();
result = (self->mStatus == NO_ERROR);
if (result && !self->mExitPending) {
......
result = self->threadLoop();
}
} else {
result = self->threadLoop();
}
if (result == false || self->mExitPending) {
......
break;
}
// Release our strong reference, to let a chance to the thread
// to die a peaceful death.
strong.clear();
// And immediately, re-acquire a strong reference for the next loop
strong = weak.promote();
} while(strong != 0);
return 0;
}
~~~
這個函數定義在文件frameworks/base/libs/utils/Threads.cpp中。
參數user指向的是一個Thread對象。這個Thread對象最終保存變量selft,用來描述前面所創建的Native線程。
Thread對象self的成員變量mHoldSelf是一個類型為sp<Thread>的智能指針,它引用的就是Thread對象self本身。因此,Thread類的靜態成員函數_threadLoop一開始就首先獲得Thread對象self的一個強引用strong和弱引用weak,目的是為了避免該Thread對象在線程運行的過程中被銷毀。關于Android系統中的智能指針,可以參考前面[Android系統的智能指針(輕量級指針、強指針和弱指針)的實現原理分析](http://blog.csdn.net/luoshengyang/article/details/6786239)一文。
Thread類的靜態成員函數_threadLoop的主體是一個while循環,這個while循環不斷地調用Thread對象self的成員函數threadLoop,來作為前面所創建的Native線程的執行主體,直到滿足以下三個條件之一:
1. Thread對象self的成員函數threadLoop的返回值等于false。
2. Thread對象self的成員函數threadLoop在執行的過程中,調用了另外一個成員函數requestExit請求前面所創建的Native線程退出,這時候Thread對象self的成員變量mExitPending的值就會等于true。
3. Thread對象self被銷毀了,即強指針strong釋放了對Thread對象self的強引用之后,弱指針weak不能成功地提升成強指針。
注意,上述while循環第一次執行的時候,在調用Thread對象self的成員函數threadLoop之前,會首先調用另外一個成員函數readyToRun,以便前面所創建的Native線程在正式運行前有機會執行一些自定義的初始化工作。
我們一般都是使用Thread子類來創建Native線程的,這時候通過重寫父類Thread的成員函數readyToRun和threadLoop,就可以使得新創建的Native線程主動地執行自定義的任務。
至此,我們就分析完成只執行C/C++代碼的Native線程的創建和運行過程了,從中我們就可以得出結論:Native線程與Dalvik虛擬機線程一樣,也是一個Linux線程。
四. 能同時執行C/C++代碼和Java代碼的Native線程的創建過程
與只能執行C/C++代碼的Native線程一樣,能同時執行C/C++代碼和Java代碼的Native線程也是可以通過C++類Thread的成員函數run來創建的,因此,接下來我們就從這個函數開始分析能同時執行C/C++代碼和Java代碼的Native線程的創建過程,如圖4所示:

圖4 能同時執行C/C++代碼和Java代碼的Native線程的創建過程
這個過程可以分為14個步驟,接下來我們就詳細分析每一個步驟。
**Step 1. Thread.run**
~~~
status_t Thread::run(const char* name, int32_t priority, size_t stack)
{
Mutex::Autolock _l(mLock);
......
bool res;
if (mCanCallJava) {
res = createThreadEtc(_threadLoop,
this, name, priority, stack, &mThread);
} else {
res = androidCreateRawThreadEtc(_threadLoop,
this, name, priority, stack, &mThread);
}
......
return NO_ERROR;
}
~~~
這個函數定義在文件frameworks/base/libs/utils/Threads.cpp中。
Thread類的成員函數run的實現可以參考前面只執行C/C++代碼的Native線程的創建過程的Step 1。不過,在我們這個場景中,當前正在處理的Thread對象的成員變量mCanCallJava的值就等于true,這時候Thread類的成員函數run就會調用另外一個函數createThreadEtc來執行創建線程的工作。因此,接下來我們就繼續分析函數createThreadEtc的實現。
**Step 2. createThreadEtc**
~~~
inline bool createThreadEtc(thread_func_t entryFunction,
void *userData,
const char* threadName = "android:unnamed_thread",
int32_t threadPriority = PRIORITY_DEFAULT,
size_t threadStackSize = 0,
thread_id_t *threadId = 0)
{
return androidCreateThreadEtc(entryFunction, userData, threadName,
threadPriority, threadStackSize, threadId) ? true : false;
}
~~~
這個函數定義在文件frameworks/base/include/utils/threads.h中。
函數createThreadEtc的實現很簡單,它通過調用函數androidCreateThreadEtc來執行創建線程的操作。
**Step 3. androidCreateThreadEtc**
~~~
static android_create_thread_fn gCreateThreadFn = androidCreateRawThreadEtc;
int androidCreateThreadEtc(android_thread_func_t entryFunction,
void *userData,
const char* threadName,
int32_t threadPriority,
size_t threadStackSize,
android_thread_id_t *threadId)
{
return gCreateThreadFn(entryFunction, userData, threadName,
threadPriority, threadStackSize, threadId);
}
~~~
這個函數定義在文件frameworks/base/libs/utils/Threads.cpp中。
函數createThreadEtc的實現也很簡單,它通調用函數指針gCreateThreadFn所指向的函數來執行創建線程的操作。注意,雖然函數指針gCreateThreadFn開始的時候指向的是函數androidCreateRawThreadEtc,但是在前面[Dalvik虛擬機的啟動過程分析](http://blog.csdn.net/luoshengyang/article/details/8885792)一文中提到,Zygote進程在啟動的過程中,會通過調用函數androidSetCreateThreadFunc將它重新指向函數javaCreateThreadEtc。因此,接下來實際上是調用的函數javaCreateThreadEtc來創建線程。
**Step 4. javaCreateThreadEtc**
~~~
/*static*/ int AndroidRuntime::javaCreateThreadEtc(
android_thread_func_t entryFunction,
void* userData,
const char* threadName,
int32_t threadPriority,
size_t threadStackSize,
android_thread_id_t* threadId)
{
void** args = (void**) malloc(3 * sizeof(void*)); // javaThreadShell must free
int result;
assert(threadName != NULL);
args[0] = (void*) entryFunction;
args[1] = userData;
args[2] = (void*) strdup(threadName); // javaThreadShell must free
result = androidCreateRawThreadEtc(AndroidRuntime::javaThreadShell, args,
threadName, threadPriority, threadStackSize, threadId);
return result;
}
~~~
這個函數定義在文件frameworks/base/core/jni/AndroidRuntime.cpp中。
函數javaCreateThreadEtc首先是將entryFunction、userData和threadName三個參數封裝在一個void*數組args中,然后再以該數組為參數,調用另外一個函數androidCreateRawThreadEtc來執行創建線程的工作。
**Step 5. androidCreateRawThreadEtc**
這個函數定義在文件frameworks/base/libs/utils/Threads.cpp中,它的具體實現可以參考前面只執行C/C++代碼的Native線程的創建過程中的Step 2。總體來說,它就是過pthread庫提供的函數pthread_create來創建一個線程,并且將從前面Step 4傳遞過來的函數AndroidRuntime類的靜態成員函數javaThreadShell作為該新創建的線程的入口點函數。因此,接下來我們就繼續分析AndroidRuntime類的靜態成員函數javaThreadShell的實現。
**Step 6. AndroidRuntime.javaThreadShell**
~~~
/*static*/ int AndroidRuntime::javaThreadShell(void* args) {
void* start = ((void**)args)[0];
void* userData = ((void **)args)[1];
char* name = (char*) ((void **)args)[2]; // we own this storage
free(args);
JNIEnv* env;
int result;
/* hook us into the VM */
if (javaAttachThread(name, &env) != JNI_OK)
return -1;
/* start the thread running */
result = (*(android_thread_func_t)start)(userData);
/* unhook us */
javaDetachThread();
free(name);
return result;
}
~~~
這個函數定義在文件frameworks/base/core/jni/AndroidRuntime.cpp中。
AndroidRuntime類的靜態成員函數javaThreadShell主要是執行以三個操作:
1. 調用函數javaAttachThread來將當前線程附加到在當前進程中運行的Dalvik虛擬機中去,使得當前線程不僅能夠執行 C/C++代碼,還可以執行Java代碼。
2. 調用函數指針start所指向的函數來作為當前線程的執行主體。函數指針start指向的函數是在前面Step 4中封裝,而被封裝的函數是從前面的Step 1傳遞過來的,也就是Thread類的靜態成員函數_threadLoop。因此,當前新創建的線程是以Thread類的靜態成員函數_threadLoop作為執行主體的。
3. 從Thread類的靜態成員函數_threadLoop返回來之后,當前線程就準備要退出了。在退出之前,需要調用函數javaDetachThread來將當前線程從當前進程中運行的Dalvik虛擬機中移除。
接下來,我們就分別分析上述三個函數的實現,以便可以了解一個能同時執行C/C++代碼和Java代碼的Native線程的初始化和運行過程。
**Step 7. javaAttachThread**
~~~
static int javaAttachThread(const char* threadName, JNIEnv** pEnv)
{
JavaVMAttachArgs args;
JavaVM* vm;
jint result;
vm = AndroidRuntime::getJavaVM();
assert(vm != NULL);
args.version = JNI_VERSION_1_4;
args.name = (char*) threadName;
args.group = NULL;
result = vm->AttachCurrentThread(pEnv, (void*) &args);
if (result != JNI_OK)
LOGI("NOTE: attach of thread '%s' failed\n", threadName);
return result;
}
~~~
這個函數定義在文件frameworks/base/core/jni/AndroidRuntime.cpp中。
函數javaAttachThread首先調用AndroidRuntime類的靜態成員函數getJavaVM來獲得在當前進程中運行的Dalvik虛擬機實例。這個Dalvik虛擬機實例是在Zygote進程啟動的過程中創建的,具體可以參考前面[Dalvik虛擬機的啟動過程分析](http://blog.csdn.net/luoshengyang/article/details/8885792)一文。
獲得了運行在當前進程中的Dalvik虛擬機實例實例之后,函數javaAttachThread接下來就可以調用它的成員函數AttachCurrentThread來將當前線程附加到它里去。同樣是從前面[Dalvik虛擬機的啟動過程分析](http://blog.csdn.net/luoshengyang/article/details/8885792)一文可以知道,這里的變量vm指向的實際上是一個JavaVMExt對象,這個JavaVMExt對象的成員函數AttachCurrentThread實際上是一個函數指針,它指向的函數為在Dalvik虛擬機內部定義的一個全局函數AttachCurrentThread。因此,接下來我們就繼續分析在Dalvik虛擬機內部定義的全局函數AttachCurrentThread的實現。
**Step 8. AttachCurrentThread**
~~~
/*
* Attach the current thread to the VM. If the thread is already attached,
* this is a no-op.
*/
static jint AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* thr_args)
{
return attachThread(vm, p_env, thr_args, false);
}
~~~
這個函數定義在文件dalvik/vm/Jni.c中。
函數AttachCurrentThread的實現很簡單,它通過調用另外一個函數attachThread來將當前線程附加到在當前進程中運行的Dalvik虛擬機中去。
**Step 9. attachThread**
~~~
static jint attachThread(JavaVM* vm, JNIEnv** p_env, void* thr_args,
bool isDaemon)
{
JavaVMAttachArgs* args = (JavaVMAttachArgs*) thr_args;
Thread* self;
bool result = false;
/*
* Return immediately if we're already one with the VM.
*/
self = dvmThreadSelf();
if (self != NULL) {
*p_env = self->jniEnv;
return JNI_OK;
}
/*
* No threads allowed in zygote mode.
*/
if (gDvm.zygote) {
return JNI_ERR;
}
/* tweak the JavaVMAttachArgs as needed */
JavaVMAttachArgs argsCopy;
if (args == NULL) {
/* allow the v1.1 calling convention */
argsCopy.version = JNI_VERSION_1_2;
argsCopy.name = NULL;
argsCopy.group = dvmGetMainThreadGroup();
} else {
assert(args->version >= JNI_VERSION_1_2);
argsCopy.version = args->version;
argsCopy.name = args->name;
if (args->group != NULL)
argsCopy.group = args->group;
else
argsCopy.group = dvmGetMainThreadGroup();
}
result = dvmAttachCurrentThread(&argsCopy, isDaemon);
......
if (result) {
self = dvmThreadSelf();
assert(self != NULL);
dvmChangeStatus(self, THREAD_NATIVE);
*p_env = self->jniEnv;
return JNI_OK;
} else {
return JNI_ERR;
}
}
~~~
這個函數定義在文件dalvik/vm/Jni.c中。
函數attachThread首先是調用函數dvmThreadSelf來檢查在當前進程中運行的Dalvik虛擬機是否已經為當前線程創建過一個Thread對象了。如果已經創建過的話,那么就說明當前線程之前已經附加到當前進程中運行的Dalvik虛擬機去了。因此,這時候函數attachThread就什么也不用做就返回了。
我們假設當前線程還沒有附加到當前進程中運行的Dalvik虛擬機中,接下來函數attachThread就繼續檢查gDvm.zygote的值是否等于true。如果等于true的話,那么就說明當前進程的Zygote進程,這時候是不允許創建附加新的線程到Dalvik虛擬機中來的。因此,這時候函數attachThread就直接返回一個錯誤碼給調用者。
通過了前面的檢查之后,函數attachThread再將根據參數thr_args的值來初始化一個JavaVMAttachArgs結構體。這個JavaVMAttachArgs結構體用來描述當前線程附加到Dalvik虛擬機時的屬性,包括它所使用的JNI版本號、線程名稱和所屬的線程組。注意,當參數thr_args的值等于NULL的時候,當前線程所使用的JNI版本號默認為JNI_VERSION_1_2,并且它的線程組被設置為當前進程的主線程組。另一方面,如果參數thr_args的值不等于NULL,那么就要求它所指定的JNI版本號大于等于JNI_VERSION_1_2,并且當它沒有指定線程組屬性性,將當前線程的線程組設置為當前進程的主線程組。
初始化好上述的JavaVMAttachArgs結構體之后,函數attachThread就調用另外一個函數dvmAttachCurrentThread來為當前線程創建一個JNI上下文環境。如果函數dvmAttachCurrentThread能成功為當前線程創建一個JNI上下文環境,那么它的返回值result就會等于ture。在這種情況下,函數dvmAttachCurrentThread同時還會為當前線程一個Thread對象。因此,這時候調用函數dvmThreadSelf的返回值就不等于NULL,并且它的成員變量jniEnv指向了一個JNIEnvExt對象,用來描述當前線程的JNI上下文環境。
最后,函數attachThread就將當前線程的狀態設置為THREAD_NATIVE,以表示它接下來都是在執行C/C++代碼,并且將前面所創建的JNI上下文環境保存在輸出參數p_env中返回給調用者使用。
接下來,我們就繼續分析函數dvmAttachCurrentThread的實現,以便可以了解Dalvik虛擬機為當前線程創建JNI上下文環境的過程。
**Step 10. dvmAttachCurrentThread**
~~~
bool dvmAttachCurrentThread(const JavaVMAttachArgs* pArgs, bool isDaemon)
{
Thread* self = NULL;
......
bool ok, ret;
self = allocThread(gDvm.stackSize);
......
setThreadSelf(self);
......
ok = prepareThread(self);
......
self->jniEnv = dvmCreateJNIEnv(self);
......
self->next = gDvm.threadList->next;
if (self->next != NULL)
self->next->prev = self;
self->prev = gDvm.threadList;
gDvm.threadList->next = self;
......
/* tell the debugger & DDM */
if (gDvm.debuggerConnected)
dvmDbgPostThreadStart(self);
return ret;
......
}
~~~
這個函數定義在文件dalvik/vm/Thread.c中。
函數dvmAttachCurrentThread主要是執行以下五個操作:
1. 調用函數allocThread和setThreadSelf為當前線程創建一個Thread對象。
2. 調用函數prepareThread來初始化當前線程所要使用到的引用表,它的具體實現可以參考前面Dalvik虛擬機線程的創建過程中的Step 5。
3. 調用函數dvmCreateJNIEnv來當前線程創建一個JNI上下文環境,它的具體實現可以參考前面[Dalvik虛擬機的啟動過程分析](http://blog.csdn.net/luoshengyang/article/details/8885792)一文。這個創建出來的JNI上下文環境使用一個JNIEnvExt結構體來描述。JNIEnvExt結構體有一個重要的成員變量funcTable,它指向的是一系列的Dalvik虛擬機回調函數。正是因為有了這個Dalvik虛擬機回調函數表,當前線程才可以訪問Dalvik虛擬機中的Java對象或者執行Dalvik虛擬機中的Java代碼。
4. 將當前線程添加到gDvm.threadList所描述的一個Dalvik虛擬機線程列表中去。從這里就可以看出,可以執行Java代碼的Native線程和Dalvik虛擬機線程一樣,都是保存在gDvm.threadList所描述的一個列表中。
5. 如果在當前進程中運行的Dalvik虛擬機有調試器連接,即gDvm.debuggerConnected的值等于true,那么就調用函數dvmDbgPostThreadStart來通知調試器在當前進程中運行的Dalvik虛擬機新增了一個線程。
這一步執行完成之后,當前線程就具有一個JNI上下文環境了。返回到前面的Step 6中,即AndroidRuntime類的靜態成員函數javaThreadShell中,接下來它就會調用Thread類的靜態成員函數_threadLoop來作為當前線程的執行主體。
**Step 11. Thread._threadLoop**
這個函數定義在文件frameworks/base/libs/utils/Threads.cpp中,它的具體實現可以參考前面只執行C/C++代碼的Native線程的創建過程的Step 3。總的來說,Thread類的靜態成員函數_threadLoop就是在一個無限循環中不斷地調用用來描述當前線程的一個Thread對象的成員函數threadLoop,直到當前線程請求退出為止。
這一步執行完成之后,當前線程就準備要退出了。返回前面的Step 6中,即AndroidRuntime類的靜態成員函數javaThreadShell中,接下來它就會調用函數javaDetachThread來將當前線程從在當前進程中運行的Dalvik虛擬機中移除。因此,接下來我們就繼續分析函數javaDetachThread的實現。
**Step 12. javaDetachThread**
~~~
/*
* Detach the current thread from the set visible to the VM.
*/
static int javaDetachThread(void)
{
JavaVM* vm;
jint result;
vm = AndroidRuntime::getJavaVM();
assert(vm != NULL);
result = vm->DetachCurrentThread();
if (result != JNI_OK)
LOGE("ERROR: thread detach failed\n");
return result;
}
~~~
這個函數定義在frameworks/base/core/jni/AndroidRuntime.cpp中。
函數javaDetachThread首先是調用AndroidRuntime類的靜態成員函數getJavaVM來獲得在當前進程中運行的Dalvik虛擬機實例,接著再調用這個Dalvik虛擬機實例的成員函數DetachCurrentThread來將當前線程將Dalvik虛擬機中移除。
在前面的Step 7中提到,AndroidRuntime類的靜態成員函數getJavaVM返回的實際上是一個JavaVMExt對象,這個JavaVMExt對象的成員函數DetachCurrentThread實際上與成員函數AttachCurrentThread一樣,也是一個函數指針,并且通過前面[Dalvik虛擬機的啟動過程分析](http://blog.csdn.net/luoshengyang/article/details/8885792)一文可以知道,這個函數指針指向的是在Dalvik虛擬機內部定義的函數DetachCurrentThread。
因此,接下來我們就繼續分在Dalvik虛擬機內部定義的函數DetachCurrentThread的實現,以便可以了解一個能同時執行C/C++代碼和Java代碼的Native線程脫離Dalvik虛擬機的過程。
**Step 13. DetachCurrentThread**
~~~
/*
* Dissociate the current thread from the VM.
*/
static jint DetachCurrentThread(JavaVM* vm)
{
......
/* detach the thread */
dvmDetachCurrentThread();
/* (no need to change status back -- we have no status) */
return JNI_OK;
}
~~~
這個函數定義在文件dalvik/vm/Jni.c中。
函數DetachCurrentThread主要是能通過調用另外一個函數dvmDetachCurrentThread來將當前線程從Dalvik虛擬機中移除,因此,接下來我們就繼續分析函數dvmDetachCurrentThread的實現。
**Step 14. dvmDetachCurrentThread**
這個函數定義在文件dalvik/vm/Thread.c中,它的具體實現可以參考前面Dalvik虛擬機線程的創建過程中的Step 10。總的來說,函數dvmDetachCurrentThread主要就是用來清理當前線程的JNI上下文環境,例如,清理當前線程還在引用的對象,以及清理當前線程所占用的內存等。
至此,我們就分析完成能同時執行C/C++代碼和Java代碼的Native線程的創建和運行過程了,從中我們就可以得出結論:能同時執行C/C++代碼和Java代碼的Native線程與只能執行C/C++代碼的Native線程一樣,都是一個Linux線程,不過區別就在于前者會被附加到Dalvik虛擬機中去,并且具有一個JNI上下文環境,因而可以執行Java代碼。
這樣,Dalvik虛擬機進程和線程的創建過程分析就分析完成了,從中我們就可以得到它們與本地操作系統的進程和線程的關系:
1. Dalvik虛擬機進程就是本地操作系統進程,也就是Linux進程,區別在于前者運行有一個Dalvik虛擬機實例。
2. Dalvik虛擬機線程就是本地操作系統進程,也就是Linux線程,區別在于前者在創建的時候會自動附加到Dalvik虛擬機中去,而后者在需要執行Java代碼的時候才會附加到Dalvik虛擬機中去。
我們可以思考一下:為什么Dalvik虛擬機要將自己的進程和線程使用本地操作系統的進程和線程來實現呢?我們知道,進程調度是一個很復雜的問題,特別是在多核的情況下,它要求高效地利用CPU資源,并且公平地調試各個進程,以達到系統吞吐量最大的目標。Linux內核本身已經實現了這種高效和公平的進程調度機制,因此,就完全沒有必要在Dalvik虛擬機內部再實現一套,這樣就要求Dalvik虛擬機使用本地操作系統的進程來作為自己的進程。此外,Linux內核沒有線程的概念,不過它可以使用一種稱為輕量級進程的概念來實現線程,這樣Dalvik虛擬機使用本地操作系統的線程來作為自己的線程,就同樣可以利用Linux內核的進程調度機制來調度它的線程,從而也能實現高效和公平的原則。
至此,我們也完成了Dalvik虛擬機的學習了,重新學習請參考Dalvik虛擬機簡要介紹和學習計劃一文。
- 前言
- Android組件設計思想
- Android源代碼開發和調試環境搭建
- Android源代碼下載和編譯
- Android源代碼情景分析法
- Android源代碼調試分析法
- 手把手教你為手機編譯ROM
- 在Ubuntu上下載、編譯和安裝Android最新源代碼
- 在Ubuntu上下載、編譯和安裝Android最新內核源代碼(Linux Kernel)
- 如何單獨編譯Android源代碼中的模塊
- 在Ubuntu上為Android系統編寫Linux內核驅動程序
- 在Ubuntu上為Android系統內置C可執行程序測試Linux內核驅動程序
- 在Ubuntu上為Android增加硬件抽象層(HAL)模塊訪問Linux內核驅動程序
- 在Ubuntu為Android硬件抽象層(HAL)模塊編寫JNI方法提供Java訪問硬件服務接口
- 在Ubuntu上為Android系統的Application Frameworks層增加硬件訪問服務
- 在Ubuntu上為Android系統內置Java應用程序測試Application Frameworks層的硬件服務
- Android源代碼倉庫及其管理工具Repo分析
- Android編譯系統簡要介紹和學習計劃
- Android編譯系統環境初始化過程分析
- Android源代碼編譯命令m/mm/mmm/make分析
- Android系統鏡像文件的打包過程分析
- 從CM刷機過程和原理分析Android系統結構
- Android系統架構概述
- Android系統整體架構
- android專用驅動
- Android硬件抽象層HAL
- Android應用程序組件
- Android應用程序框架
- Android用戶界面架構
- Android虛擬機之Dalvik虛擬機
- Android硬件抽象層
- Android硬件抽象層(HAL)概要介紹和學習計劃
- Android專用驅動
- Android Logger驅動系統
- Android日志系統驅動程序Logger源代碼分析
- Android應用程序框架層和系統運行庫層日志系統源代碼分析
- Android日志系統Logcat源代碼簡要分析
- Android Binder驅動系統
- Android進程間通信(IPC)機制Binder簡要介紹和學習計劃
- 淺談Service Manager成為Android進程間通信(IPC)機制Binder守護進程之路
- 淺談Android系統進程間通信(IPC)機制Binder中的Server和Client獲得Service Manager接口之路
- Android系統進程間通信(IPC)機制Binder中的Server啟動過程源代碼分析
- Android系統進程間通信(IPC)機制Binder中的Client獲得Server遠程接口過程源代碼分析
- Android系統進程間通信Binder機制在應用程序框架層的Java接口源代碼分析
- Android Ashmem驅動系統
- Android系統匿名共享內存Ashmem(Anonymous Shared Memory)簡要介紹和學習計劃
- Android系統匿名共享內存Ashmem(Anonymous Shared Memory)驅動程序源代碼分析
- Android系統匿名共享內存Ashmem(Anonymous Shared Memory)在進程間共享的原理分析
- Android系統匿名共享內存(Anonymous Shared Memory)C++調用接口分析
- Android應用程序進程管理
- Android應用程序進程啟動過程的源代碼分析
- Android系統進程Zygote啟動過程的源代碼分析
- Android系統默認Home應用程序(Launcher)的啟動過程源代碼分析
- Android應用程序消息機制
- Android應用程序消息處理機制(Looper、Handler)分析
- Android應用程序線程消息循環模型分析
- Android應用程序輸入事件分發和處理機制
- Android應用程序鍵盤(Keyboard)消息處理機制分析
- Android應用程序UI架構
- Android系統的開機畫面顯示過程分析
- Android幀緩沖區(Frame Buffer)硬件抽象層(HAL)模塊Gralloc的實現原理分析
- SurfaceFlinger
- Android系統Surface機制的SurfaceFlinger服務
- SurfaceFlinger服務簡要介紹和學習計劃
- 啟動過程分析
- 對幀緩沖區(Frame Buffer)的管理分析
- 線程模型分析
- 渲染應用程序UI的過程分析
- Android應用程序與SurfaceFlinger服務的關系
- 概述和學習計劃
- 連接過程分析
- 共享UI元數據(SharedClient)的創建過程分析
- 創建Surface的過程分析
- 渲染Surface的過程分析
- Android應用程序窗口(Activity)
- 實現框架簡要介紹和學習計劃
- 運行上下文環境(Context)的創建過程分析
- 窗口對象(Window)的創建過程分析
- 視圖對象(View)的創建過程分析
- 與WindowManagerService服務的連接過程分析
- 繪圖表面(Surface)的創建過程分析
- 測量(Measure)、布局(Layout)和繪制(Draw)過程分析
- WindowManagerService
- WindowManagerService的簡要介紹和學習計劃
- 計算Activity窗口大小的過程分析
- 對窗口的組織方式分析
- 對輸入法窗口(Input Method Window)的管理分析
- 對壁紙窗口(Wallpaper Window)的管理分析
- 計算窗口Z軸位置的過程分析
- 顯示Activity組件的啟動窗口(Starting Window)的過程分析
- 切換Activity窗口(App Transition)的過程分析
- 顯示窗口動畫的原理分析
- Android控件TextView的實現原理分析
- Android視圖SurfaceView的實現原理分析
- Android應用程序UI硬件加速渲染
- 簡要介紹和學習計劃
- 環境初始化過程分析
- 預加載資源地圖集服務(Asset Atlas Service)分析
- Display List構建過程分析
- Display List渲染過程分析
- 動畫執行過程分析
- Android應用程序資源管理框架
- Android資源管理框架(Asset Manager)
- Asset Manager 簡要介紹和學習計劃
- 編譯和打包過程分析
- Asset Manager的創建過程分析
- 查找過程分析
- Dalvik虛擬機和ART虛擬機
- Dalvik虛擬機
- Dalvik虛擬機簡要介紹和學習計劃
- Dalvik虛擬機的啟動過程分析
- Dalvik虛擬機的運行過程分析
- Dalvik虛擬機JNI方法的注冊過程分析
- Dalvik虛擬機進程和線程的創建過程分析
- Dalvik虛擬機垃圾收集機制簡要介紹和學習計劃
- Dalvik虛擬機Java堆創建過程分析
- Dalvik虛擬機為新創建對象分配內存的過程分析
- Dalvik虛擬機垃圾收集(GC)過程分析
- ART虛擬機
- Android ART運行時無縫替換Dalvik虛擬機的過程分析
- Android運行時ART簡要介紹和學習計劃
- Android運行時ART加載OAT文件的過程分析
- Android運行時ART加載類和方法的過程分析
- Android運行時ART執行類方法的過程分析
- ART運行時垃圾收集機制簡要介紹和學習計劃
- ART運行時Java堆創建過程分析
- ART運行時為新創建對象分配內存的過程分析
- ART運行時垃圾收集(GC)過程分析
- ART運行時Compacting GC簡要介紹和學習計劃
- ART運行時Compacting GC堆創建過程分析
- ART運行時Compacting GC為新創建對象分配內存的過程分析
- ART運行時Semi-Space(SS)和Generational Semi-Space(GSS)GC執行過程分析
- ART運行時Mark-Compact( MC)GC執行過程分析
- ART運行時Foreground GC和Background GC切換過程分析
- Android安全機制
- SEAndroid安全機制簡要介紹和學習計劃
- SEAndroid安全機制框架分析
- SEAndroid安全機制中的文件安全上下文關聯分析
- SEAndroid安全機制中的進程安全上下文關聯分析
- SEAndroid安全機制對Android屬性訪問的保護分析
- SEAndroid安全機制對Binder IPC的保護分析
- 從NDK在非Root手機上的調試原理探討Android的安全機制
- APK防反編譯
- Android視頻硬解穩定性問題探討和處理
- Android系統的智能指針(輕量級指針、強指針和弱指針)的實現原理分析
- Android應用程序安裝過程源代碼分析
- Android應用程序啟動過程源代碼分析
- 四大組件源代碼分析
- Activity
- Android應用程序的Activity啟動過程簡要介紹和學習計劃
- Android應用程序內部啟動Activity過程(startActivity)的源代碼分析
- 解開Android應用程序組件Activity的"singleTask"之謎
- Android應用程序在新的進程中啟動新的Activity的方法和過程分析
- Service
- Android應用程序綁定服務(bindService)的過程源代碼分析
- ContentProvider
- Android應用程序組件Content Provider簡要介紹和學習計劃
- Android應用程序組件Content Provider應用實例
- Android應用程序組件Content Provider的啟動過程源代碼分析
- Android應用程序組件Content Provider在應用程序之間共享數據的原理分析
- Android應用程序組件Content Provider的共享數據更新通知機制分析
- BroadcastReceiver
- Android系統中的廣播(Broadcast)機制簡要介紹和學習計劃
- Android應用程序注冊廣播接收器(registerReceiver)的過程分析
- Android應用程序發送廣播(sendBroadcast)的過程分析