Android Zygoteプロセスとappプロセスforkプロセス分析2
16751 ワード
プロセスforkのいくつかの知識
appプロセスforkを分析する際には、プロセスforkに関する知識を簡単に普及させ、forkサブプロセスと親プロセスの関係について大神の説明を引用する[forkから出たサブプロセスと親プロセス]:http://blog.csdn.net/theone10211024/article/details/13774669
forkによって作成された新しいプロセスは、サブプロセス(child process)と呼ばれます.この関数は1回呼び出されますが、2回返されます.2回の戻りの違いは、サブプロセスの戻り値が0であり、親プロセスの戻り値が新しいプロセス(サブプロセス)のプロセスidであることです.サブプロセスidを親プロセスに返す理由は、1つのプロセスのサブプロセスが1つより多く、1つのプロセスがすべてのサブプロセスのプロセスidを取得できる関数がないためです.サブプロセスにとってforkが0を返すのは、getpid()をいつでも呼び出して自分のpidを取得できるからである.親プロセスのidを取得するためにgetppid()を呼び出すこともできます.(プロセスid 0は常に交換プロセスによって使用されるため、サブプロセスのプロセスidが0になることは不可能である).
forkの後、オペレーティングシステムは親プロセスと完全に同じサブプロセスをコピーします.親子関係とはいえ、オペレーティングシステムから見れば、兄弟関係のように見えます.この2つのプロセスはコード空間を共有していますが、データ空間は互いに独立しており、サブプロセスデータ空間の内容は親プロセスの完全なコピーであり、命令ポインタも完全に同じです.サブプロセスには、親プロセスが現在実行されている場所(両プロセスのプログラムカウンタpc値が同じ、すなわち、サブプロセスはfork戻りから実行される)がありますが、forkが成功した場合、サブプロセスのforkの戻り値は0、親プロセスのforkの戻り値はサブプロセスのプロセス番号であり、forkが成功しない場合、親プロセスはエラーを返します.2つのプロセスが同時に実行され,歩調が一致し,forkの後,それぞれ異なる仕事,すなわち分岐していることが想像できる.これもforkがforkと呼ぶ理由です
それでは今から分析を行います
Zygoteプロセスにfork appプロセスリクエストを送信
まずActivityManagerServiceの
コードは長すぎて、私たちは肝心なところだけを見て、
上の
上のコードは実は
コードも簡単です.クライアントとserverはすでにsocket接続を確立しています.この方法では、すべてのパラメータをsocketを通じて
Zygoteプロセスforkリクエストの処理
前節で述べたように、clientが送信するsocket接続の確立は、最終的に
渡されたパラメータが最終的に呼び出された
この方法は
最終的には
異常を投げ出す方式で呼び出し,主な目的は現在のスレッドのスタック情報を空にすることである.
OK,Zygoteプロセスとappプロセスforkプロセス解析についてはここまで
appプロセスforkを分析する際には、プロセスforkに関する知識を簡単に普及させ、forkサブプロセスと親プロセスの関係について大神の説明を引用する[forkから出たサブプロセスと親プロセス]:http://blog.csdn.net/theone10211024/article/details/13774669
forkによって作成された新しいプロセスは、サブプロセス(child process)と呼ばれます.この関数は1回呼び出されますが、2回返されます.2回の戻りの違いは、サブプロセスの戻り値が0であり、親プロセスの戻り値が新しいプロセス(サブプロセス)のプロセスidであることです.サブプロセスidを親プロセスに返す理由は、1つのプロセスのサブプロセスが1つより多く、1つのプロセスがすべてのサブプロセスのプロセスidを取得できる関数がないためです.サブプロセスにとってforkが0を返すのは、getpid()をいつでも呼び出して自分のpidを取得できるからである.親プロセスのidを取得するためにgetppid()を呼び出すこともできます.(プロセスid 0は常に交換プロセスによって使用されるため、サブプロセスのプロセスidが0になることは不可能である).
forkの後、オペレーティングシステムは親プロセスと完全に同じサブプロセスをコピーします.親子関係とはいえ、オペレーティングシステムから見れば、兄弟関係のように見えます.この2つのプロセスはコード空間を共有していますが、データ空間は互いに独立しており、サブプロセスデータ空間の内容は親プロセスの完全なコピーであり、命令ポインタも完全に同じです.サブプロセスには、親プロセスが現在実行されている場所(両プロセスのプログラムカウンタpc値が同じ、すなわち、サブプロセスはfork戻りから実行される)がありますが、forkが成功した場合、サブプロセスのforkの戻り値は0、親プロセスのforkの戻り値はサブプロセスのプロセス番号であり、forkが成功しない場合、親プロセスはエラーを返します.2つのプロセスが同時に実行され,歩調が一致し,forkの後,それぞれ異なる仕事,すなわち分岐していることが想像できる.これもforkがforkと呼ぶ理由です
それでは今から分析を行います
Zygoteプロセスにfork appプロセスリクエストを送信
まずActivityManagerServiceの
startProcessLocked
メソッドを見てみましょう.これが最初のエントリです. private final void startProcessLocked(ProcessRecord app, String hostingType,
String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
......
Process.ProcessStartResult startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, debugFlags, mountExternal,
app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
app.info.dataDir, entryPointArgs);
......
}
コードは長すぎて、私たちは肝心なところだけを見て、
Process.start
この方法はforkを始めて、okそれではその内容を見て、コードはとても長くて、直接以下のこのセグメントのコードの総括を見ることができます public static final ProcessStartResult start(final String processClass,
final String niceName,
int uid, int gid, int[] gids,
int debugFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String abi,
String instructionSet,
String appDataDir,
String[] zygoteArgs) {
try {
return startViaZygote(processClass, niceName, uid, gid, gids,
debugFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG,
"Starting VM process through Zygote failed");
throw new RuntimeException(
"Starting VM process through Zygote failed", ex);
}
}
private static ProcessStartResult startViaZygote(final String processClass,
final String niceName,
final int uid, final int gid,
final int[] gids,
int debugFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String abi,
String instructionSet,
String appDataDir,
String[] extraArgs)
throws ZygoteStartFailedEx {
synchronized(Process.class) {
ArrayList argsForZygote = new ArrayList();
// --runtime-init, --setuid=, --setgid=,
// and --setgroups= must go first
argsForZygote.add("--runtime-init");
argsForZygote.add("--setuid=" + uid);
argsForZygote.add("--setgid=" + gid);
if ((debugFlags & Zygote.DEBUG_ENABLE_JNI_LOGGING) != 0) {
argsForZygote.add("--enable-jni-logging");
}
if ((debugFlags & Zygote.DEBUG_ENABLE_SAFEMODE) != 0) {
argsForZygote.add("--enable-safemode");
}
if ((debugFlags & Zygote.DEBUG_ENABLE_DEBUGGER) != 0) {
argsForZygote.add("--enable-debugger");
}
if ((debugFlags & Zygote.DEBUG_ENABLE_CHECKJNI) != 0) {
argsForZygote.add("--enable-checkjni");
}
if ((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) != 0) {
argsForZygote.add("--enable-assert");
}
if (mountExternal == Zygote.MOUNT_EXTERNAL_MULTIUSER) {
argsForZygote.add("--mount-external-multiuser");
} else if (mountExternal == Zygote.MOUNT_EXTERNAL_MULTIUSER_ALL) {
argsForZygote.add("--mount-external-multiuser-all");
}
argsForZygote.add("--target-sdk-version=" + targetSdkVersion);
//TODO optionally enable debuger
//argsForZygote.add("--enable-debugger");
// --setgroups is a comma-separated list
if (gids != null && gids.length > 0) {
StringBuilder sb = new StringBuilder();
sb.append("--setgroups=");
int sz = gids.length;
for (int i = 0; i < sz; i++) {
if (i != 0) {
sb.append(',');
}
sb.append(gids[i]);
}
argsForZygote.add(sb.toString());
}
if (niceName != null) {
argsForZygote.add("--nice-name=" + niceName);
}
if (seInfo != null) {
argsForZygote.add("--seinfo=" + seInfo);
}
if (instructionSet != null) {
argsForZygote.add("--instruction-set=" + instructionSet);
}
if (appDataDir != null) {
argsForZygote.add("--app-data-dir=" + appDataDir);
}
argsForZygote.add(processClass);
if (extraArgs != null) {
for (String arg : extraArgs) {
argsForZygote.add(arg);
}
}
return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
}
}
上の
startViaZygote
メソッドでは、パラメータを最終的にリストに入れ、zygoteSendArgsAndGetResult
メソッドを呼び出します.このメソッドの最初のパラメータはopenZygoteSocketIfNeeded(abi)
メソッドを呼び出します.では、まず、このメソッドの内容を見てみましょう. private static ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {
if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
try {
primaryZygoteState = ZygoteState.connect(ZYGOTE_SOCKET);
} catch (IOException ioe) {
throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);
}
}
......
}
ZygoteState.connect(ZYGOTE_SOCKET)
この方法を見てみましょう /* ZygoteInit server socket */
public static ZygoteState connect(String socketAddress) throws IOException {
DataInputStream zygoteInputStream = null;
BufferedWriter zygoteWriter = null;
final LocalSocket zygoteSocket = new LocalSocket();
try {
zygoteSocket.connect(new LocalSocketAddress(socketAddress,
LocalSocketAddress.Namespace.RESERVED));
zygoteInputStream = new DataInputStream(zygoteSocket.getInputStream());
zygoteWriter = new BufferedWriter(new OutputStreamWriter(
zygoteSocket.getOutputStream()), 256);
} catch (IOException ex) {
try {
zygoteSocket.close();
} catch (IOException ignore) {
}
throw ex;
}
String abiListString = getAbiList(zygoteWriter, zygoteInputStream);
Log.i("Zygote", "Process: zygote socket opened, supported ABIS: " + abiListString);
return new ZygoteState(zygoteSocket, zygoteInputStream, zygoteWriter,
Arrays.asList(abiListString.split(",")));
}
上のコードは実は
ZygoteInit
クラスのServer Socketと接続を確立し、socketが接続されていれば、通信が可能になります.zygoteSendArgsAndGetResult
メソッドに戻ります private static ProcessStartResult zygoteSendArgsAndGetResult(
ZygoteState zygoteState, ArrayList args)
throws ZygoteStartFailedEx {
try {
/**
* See com.android.internal.os.ZygoteInit.readArgumentList()
* Presently the wire format to the zygote process is:
* a) a count of arguments (argc, in essence)
* b) a number of newline-separated argument strings equal to count
*
* After the zygote process reads these it will write the pid of
* the child or -1 on failure, followed by boolean to
* indicate whether a wrapper process was used.
*/
final BufferedWriter writer = zygoteState.writer;
final DataInputStream inputStream = zygoteState.inputStream;
writer.write(Integer.toString(args.size()));
writer.newLine();
int sz = args.size();
for (int i = 0; i < sz; i++) {
String arg = args.get(i);
if (arg.indexOf('
') >= 0) {
throw new ZygoteStartFailedEx(
"embedded newlines not allowed");
}
writer.write(arg);
writer.newLine();
}
writer.flush();
// Should there be a timeout on this?
ProcessStartResult result = new ProcessStartResult();
result.pid = inputStream.readInt();
/*pid 0 ,==0 ,》0 */
if (result.pid < 0) {
throw new ZygoteStartFailedEx("fork() failed");
}
result.usingWrapper = inputStream.readBoolean();
return result;
} catch (IOException ex) {
zygoteState.close();
throw new ZygoteStartFailedEx(ex);
}
}
コードも簡単です.クライアントとserverはすでにsocket接続を確立しています.この方法では、すべてのパラメータをsocketを通じて
ZygoteInit
のサーバSocketに送信し、送信が完了したら、サーバSocketが結果を返すのを待っています.Zygoteプロセスforkリクエストの処理
前節で述べたように、clientが送信するsocket接続の確立は、最終的に
ZygoteInit
にZygoteConnection
オブジェクトを作成し、clientが送信するfork要求を受信すると、ZygoteConnection
オブジェクトのrunOnce
メソッドが呼び出されるので、この方法を見ると boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
.......
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
parsedArgs.niceName, fdsToClose, parsedArgs.instructionSet,
parsedArgs.appDataDir);
......
try {
/* pid==0 , else */
if (pid == 0) {
/* */
// in child
IoUtils.closeQuietly(serverPipeFd);
serverPipeFd = null;
handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
// should never get here, the child is expected to either
// throw ZygoteInit.MethodAndArgsCaller or exec().
return true;
} else {
// in parent...pid of < 0 means failure
IoUtils.closeQuietly(childPipeFd);
childPipeFd = null;
/*Process io pid */
return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
}
} finally {
IoUtils.closeQuietly(childPipeFd);
IoUtils.closeQuietly(serverPipeFd);
}
}
Zygote.forkAndSpecialize
このメソッドはnativeメソッドを呼び出してfork appプロセスを呼び出し、forkが成功すると、サブプロセスは基本的に親プロセスのすべてのデータなどをコピーする.これは本節の開始時にこの知識を科学的に普及させ、サブプロセスforkが出したpid==0であるため、if(pid == 0){}else{}
というコードは特に興味深い.pid==0はサブプロセス実行であり、elseは親プロセス実行である.親プロセスが実行するコードは貼らないで、それは主にforkの成功したpidをclient端に返して、この時ActivityManagerService
のstartProcessLocked
は実行を続けることができます.サブプロセスが実行するコードを見てみましょう.最終的にはhandleChildProc
メソッドが実行されます. private void handleChildProc(Arguments parsedArgs,
FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
throws ZygoteInit.MethodAndArgsCaller {
......
if (parsedArgs.runtimeInit) {
if (parsedArgs.invokeWith != null) {
WrapperInit.execApplication(parsedArgs.invokeWith,
parsedArgs.niceName, parsedArgs.targetSdkVersion,
pipeFd, parsedArgs.remainingArgs);
} else {
RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,
parsedArgs.remainingArgs, null /* classLoader */);
}
} else {
......
}
}
渡されたパラメータが最終的に呼び出された
RuntimeInit.zygoteInit
メソッドを位置決めできることから、 public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
throws ZygoteInit.MethodAndArgsCaller {
if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote");
redirectLogStreams();
commonInit();
nativeZygoteInit();
applicationInit(targetSdkVersion, argv, classLoader);
}
この方法は
applicationInit(targetSdkVersion, argv, classLoader)
という方法にのみ注目している. private static void applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
throws ZygoteInit.MethodAndArgsCaller {
// If the application calls System.exit(), terminate the process
// immediately without running any shutdown hooks. It is not possible to
// shutdown an Android application gracefully. Among other things, the
// Android runtime shutdown hooks close the Binder driver, which can cause
// leftover running threads to crash before the process actually exits.
nativeSetExitWithoutCleanup(true);
// We want to be fairly aggressive about heap utilization, to avoid
// holding on to a lot of memory that isn't needed.
VMRuntime.getRuntime().setTargetHeapUtilization(0.75f);
VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
final Arguments args;
try {
args = new Arguments(argv);
} catch (IllegalArgumentException ex) {
Slog.e(TAG, ex.getMessage());
// let the process exit
return;
}
// Remaining arguments are passed to the start class's static main
invokeStaticMain(args.startClass, args.startArgs, classLoader);
}
private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader)
throws ZygoteInit.MethodAndArgsCaller {
Class> cl;
try {
cl = Class.forName(className, true, classLoader);
} catch (ClassNotFoundException ex) {
throw new RuntimeException(
"Missing class when invoking static main " + className,
ex);
}
Method m;
try {
m = cl.getMethod("main", new Class[] { String[].class });
} catch (NoSuchMethodException ex) {
throw new RuntimeException(
"Missing static main on " + className, ex);
} catch (SecurityException ex) {
throw new RuntimeException(
"Problem getting static main on " + className, ex);
}
int modifiers = m.getModifiers();
if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
throw new RuntimeException(
"Main method is not public and static on " + className);
}
/*
* This throw gets caught in ZygoteInit.main(), which responds
* by invoking the exception's run() method. This arrangement
* clears up all the stack frames that were required in setting
* up the process.
*/
throw new ZygoteInit.MethodAndArgsCaller(m, argv);
}
最終的には
invokeStaticMain
という方法に注目し,この方法は最終的にZygoteInit.MethodAndArgsCaller(m, argv)
の異常を放出し,この異常はActivityThread
のmain
の方法を反射する.前節ZygoteInit
のmain
の方法を覚えていますか? public static void main(String argv[]){
try{
.....
} catch (MethodAndArgsCaller caller) {
caller.run();
} catch (RuntimeException ex) {
Log.e(TAG, "Zygote died with exception", ex);
closeServerSocket();
throw ex;
}
}
main
メソッドは最終的にMethodAndArgsCaller
の異常をキャプチャし、キャプチャ後は最終的にActivityThread
を呼び出すmain
メソッドである.異常を投げ出す方式で呼び出し,主な目的は現在のスレッドのスタック情報を空にすることである.
OK,Zygoteプロセスとappプロセスforkプロセス解析についてはここまで