編輯:關於Android編程
我們知道,在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應用程序進程啟動過程的源代碼分析一文可以知道,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
[java]
public class Zygote {
......
native public static int forkAndSpecialize(int uid, int gid, int[] gids,
int debugFlags, int[][] rlimits);
......
}
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來實現的,如下所示:
[cpp]
/* 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);
}
/* 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
[cpp]
/*
* 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;
}
/*
* 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啟動過程的源代碼分析一文。
當函數forkAndSpecializeCommon是調用來創建System進程的時候,參數isSystemServer的值就等於true,這時候在參數列表args就會包含兩個額外的參數permittedCapabilities和effectiveCapabilities。其中,permittedCapabilities表示System進程允許的特權,而effectiveCapabilities表示System進程當前的有效特權,這兩個參數的關系就類似於進程的uid和euid的關系一樣。
進程的特權是什麼概念呢?從Linux 2.2開始,Root用戶的權限被劃分成了一系列的子權限,每一個子權限都通過一個bit來表示,這些bit的定義可以參考CAPABILITIES(7)。每一個進程都關聯有一個u32整數,用來描述它所具有的Root用戶子權限,我們可以通過系統調用capset來進行設置。
參考前面Android系統進程Zygote啟動過程的源代碼分析一篇文章可以知道,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虛擬機的啟動過程分析一文可以知道,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虛擬機的啟動過程分析一文可以知道,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
[cpp]
/*
* 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;
}
/*
* 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
[java]
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);
}
......
}
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
[java]
class VMThread
{
......
native static void create(Thread t, long stacksize);
......
}
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來實現的,如下所示:
[cpp]
/*
* 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();
}
/*
* 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
[cpp]
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;
}
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中。
用戶登錄注冊界面開發及用戶信息管理案例詳解剛開始接觸Android編程,這算是我寫的第一個簡單工程,主要功能有:用戶登錄、注冊、注銷、修改密碼、記住密碼共5個基本操作,其
實驗中只需要編寫相應的xml的代碼,java代碼不需要更改,因為我們這裡只是練習android的界面設計。線性布局:線性布局就是將各種控件按照行或者列依次進行排列。其中本
最近下了個攜程App,點開首頁看,注意到其按鈕在點擊的時候並不是我們經常看到的變色效果,而是先收縮,放開時,再回到原來的大小,感覺這個效果雖然小,但是感覺非常新穎,於是決
我們知道在Android系統中,我們執行完耗時操作都要另外開啟子線程來執行,執行完線程以後線程會自動銷毀。想象一下如果我們在項目中經常要執行耗時操作,如果經常要開啟線程,