blog

Zygoteプロセス起動時のプロセス解析

Initプロセスは、Androidシステムのユーザー空間における最初のプロセスであり、その最も重要な仕事はzygoteプロセスを作成することです。Androidシステムでは、すべてのアプリケーションプ...

May 5, 2020 · 12 min. read
シェア

zygoteプロセスの定義

Initプロセスは、Androidシステムのユーザー空間における最初のプロセスであり、その最も重要な仕事はzygoteプロセスを作成することです。Androidでは、すべてのアプリケーションプロセスとシステムサービスプロセスSystemServerは、Zygoteプロセスによって生成されます。この記事では、Zygoteの起動プロセスを分析します。

そのディレクトリはsystem/core/init/init.cppにあり、そこで属性サービスを開始し、init.rcファイルを解析します。init.rcファイルはAndroidシステムの設定ファイルで、以下のフォーマットになっています:

system/core/rootdir/init.rc
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
 class main
 socket zygote stream 660 root system
 onrestart write /sys/android_power/request_state wake
 onrestart write /sys/power/state on
 onrestart restart media
 onrestart restart netd

各serviceコマンドは、initプロセスに新しいプロセスを作成するためにfork関数を呼び出すように促します。serviceはここで、/system/bin/app_processで実行されるzygoteという名前のプロセスを作成するようにinitプロセスに通知するために使用され、その後にapp_processの引数が続きます。後者は、app_processに渡すパラメータです。 class mainは、mainとしてzygoteのクラス名を参照します。

frameworks/base/cmds/app_process/app_main.cpp

int main(int argc, char* const argv[])
{
 
runtime.start("com.android.internal.os.ZygoteInit",
 startSystemServer ? "start-system-server" : "");
 
}

// これは、仮想マシンを起動し、callNameで定義された静的メインメソッドを呼び出すことを含みます。

frameworks/base/core/jni/AndroidRuntime.cpp

void AndroidRuntime::start(const char* className, const char* options)
{
  
 JNIEnv* env;
 if (startVm(&mJavaVM, &env) != 0) {//仮想マシンの起動
 return;
 }
 onVmCreated(env);
 /*
 * Register android functions.
 */
 if (startReg(env) < 0) {//仮想マシンに関数を登録する
 ALOGE("Unable to register all android natives
");
 return;
 }
 /*
 * Start VM. This thread becomes the main thread of the VM, and will
 * not return until the VM exits.
 */
 char* slashClassName = toSlashClassName(className);
 jclass startClass = env->FindClass(slashClassName);//ZygoteInitクラスを見つける
 if (startClass == NULL) {
 ALOGE("JavaVM unable to locate class '%s'
", slashClassName);
 /* keep going */
 } else {
 jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
 "([Ljava/lang/String;)V");//静的メイン・メソッドを呼び出す
 }
}

Zygoteプロセスは起動時にJavaVMを作成するので、fockによって作成されたアプリケーションプロセスとSystemServerプロセスは、内部でJavaVMインスタンスのコピーを取得することができます。zygoteを起動するときに渡されるクラスはcom.android.internal.os.ZygoteInitで、mainメソッドはリフレクションを通して呼び出され、起動時の初期化処理をさらに完了させます。

//AndroidRuntimeのstartメソッドは、リフレクションによって呼び出される。
public static void main(String argv[]) {
 try {
 // Start profiling the zygote initialization.
 SamplingProfilerIntegration.start();
 registerZygoteSocket();//zygoteローカルソケットサービスを登録し、AMSからのアプリケーションプロセス作成要求を待つ。
 EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
 SystemClock.uptimeMillis());
 preload();//いくつかのリソースやクラスなどをプリロードする。
  
 if (argv[1].equals("start-system-server")) {
 startSystemServer();//SystemServerプロセスを開始する
 } else if (!argv[1].equals("")) {
 throw new RuntimeException(argv[0] + USAGE_STRING);
 }
 Log.i(TAG, "Accepting command socket connections");
 runSelectLoop();//クライアントのリクエストを待つ
 closeServerSocket();
 } catch (MethodAndArgsCaller caller) {
 caller.run();//
 } catch (RuntimeException ex) {
 Log.e(TAG, "Zygote died with exception", ex);
 closeServerSocket();
 throw ex;
 }
}

接合子の初期化には次のようなイベントがあります:

  1. ソケットサービスをリッスンし、AMSがアプリケーションリクエストを作成するのを待ちます。
  2. SystemServer プロセスの作成

SystemServer プロセスの生成とアプリケーションプロセスの生成の両方が、MethodAndArgsCaller 例外をキャッチすることで、引数で指定されたクラスの main メソッドを実行することは注目に値します。

ソケットサービスを登録するコードに移りましょう。このコードでは、LocalServerSocketローカルソケットオブジェクトを作成するだけです。

private static void registerZygoteSocket() {
 if (sServerSocket == null) {
 int fileDesc;
 try {
 String env = System.getenv(ANDROID_SOCKET_ENV);
 fileDesc = Integer.parseInt(env);
 } catch (RuntimeException ex) {
 throw new RuntimeException(
 ANDROID_SOCKET_ENV + " unset or invalid", ex);
 }
 try {
 sServerSocket = new LocalServerSocket(
 createFileDescriptor(fileDesc));
 } catch (IOException ex) {
 throw new RuntimeException(
 "Error binding to local socket '" + fileDesc + "'", ex);
 }
 }
}

SystemServerプロセス作成プロセス

ここでは SystemServer の作成プロセスに焦点を当て、startSystemServer を続けます。

private static boolean startSystemServer()
 throws MethodAndArgsCaller, RuntimeException {
  
 /* Hardcoded command line to start the system server */
 String args[] = {
 "--setuid=1000",
 "--setgid=1000",
 "--setgroups=,,,,,,,,3007",
 "--capabilities=" + capabilities + "," + capabilities,
 "--runtime-init",
 "--nice-name=system_server",
 "com.android.server.SystemServer",
 };
 ZygoteConnection.Arguments parsedArgs = null;
 int pid;
 try {
 parsedArgs = new ZygoteConnection.Arguments(args);
 ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
 ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
 /* Request to fork the system server process */
 pid = Zygote.forkSystemServer(
 parsedArgs.uid, parsedArgs.gid,
 parsedArgs.gids,
 parsedArgs.debugFlags,
 null,
 parsedArgs.permittedCapabilities,
 parsedArgs.effectiveCapabilities);//fork systemServerプロセスから出る
 } catch (IllegalArgumentException ex) {
 throw new RuntimeException(ex);
 }
 /* For child process */
 if (pid == 0) {
 handleSystemServerProcess(parsedArgs);//サブプロセス呼び出しでは
 }
 return true;
}

frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

private static void handleSystemServerProcess(//SystemServerプロセスでのさらなる作業
 ZygoteConnection.Arguments parsedArgs)
 throws ZygoteInit.MethodAndArgsCaller {
 closeServerSocket();//親プロセスから継承したソケットを閉じる
 // set umask to 0077 so new files and directories will default to owner-only permissions.
 Libcore.os.umask(S_IRWXG | S_IRWXO);
 if (parsedArgs.niceName != null) {
 Process.setArgV0(parsedArgs.niceName);
 }
 if (parsedArgs.invokeWith != null) {
 WrapperInit.execApplication(parsedArgs.invokeWith,
 parsedArgs.niceName, parsedArgs.targetSdkVersion,
 null, parsedArgs.remainingArgs);
 } else {
 /*
 * Pass the remaining arguments to SystemServer.
 */
 //この時点で、maintainArgsは”com.android.server.SystemServer”
 RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs);
 }
 /* should never reach here */
}

以下は、初期化作業を行うためのzygoteInitの呼び出しです。

frameworks/base/core/java/com/android/internal/os/RuntimeInit.java

public static final void zygoteInit(int targetSdkVersion, String[] argv)
 throws ZygoteInit.MethodAndArgsCaller {
 if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote");
 redirectLogStreams();
 commonInit();
 nativeZygoteInit();//zygoteローカル初期化
 applicationInit(targetSdkVersion, argv);////アプリケーション層の初期化
}

まず、AndroidRuntimeで定義されている最初のメソッドnativeZygoteInitの呼び出しを見てみましょう。

/frameworks/base/core/jni/AndroidRuntime.cpp

static void
 com_android_internal_os_RuntimeInit_nativeZygoteInit(JNIEnv* env, jobject clazz)
{
 gCurRuntime->onZygoteInit();
}

ここでonZygoteInitはそのサブクラスAppRuntimeメソッドを呼び出し、zygote startはこのクラスを通してstartメソッドを呼び出し実行します。

frameworks/base/cmds/app_process/app_main.cpp

virtual void onZygoteInit()
{
 // Re-enable tracing now that we're no longer in Zygote.
 atrace_set_tracing_enabled(true);
 sp<ProcessState> proc = ProcessState::self();
 ALOGV("App process: starting thread pool.
");
 proc->startThreadPool();
}

ProcessState はバインダ機構にとって非常に重要で、主に SystemServer プロセスのバインダ環境を初期化し、プロセス内のバインダを通して他のプロセスと通信できるようにします。

ここで2番目のメソッドapplicationInitを参照してください、このメソッドの2番目のパラメータは、呼び出されたクラスと関連するメソッドのパラメータに関する情報が含まれています、ここではcom.android.server.SystemServerとメインメソッドです。

frameworks/base/core/java/com/android/internal/os/RuntimeInit.java

private static void applicationInit(int targetSdkVersion, String[] argv)
 throws ZygoteInit.MethodAndArgsCaller {
  
 // Remaining arguments are passed to the start class's static main
 invokeStaticMain(args.startClass, args.startArgs);
 }

このメソッドは内部的に invokeStaticMain メソッドを呼び出します。

//このメソッドは最終的に例外MethodAndArgsCallerをスローし、そのrunメソッドの呼び出しをトリガーする。
private static void invokeStaticMain(String className, String[] argv)
 throws ZygoteInit.MethodAndArgsCaller {
 Class<?> cl;
 try {
 cl = Class.forName(className);//クラス情報の取得 SystemServerかアクティビティ Threadの可能性がある。
 } catch (ClassNotFoundException ex) {
 }
 Method m;
 try {
 m = cl.getMethod("main", new Class[] { String[].class });//対応するクラスのmainメソッドを取得する
 } catch (){} 
 
 /*
 * 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.
 */
 //例外がスローされ、MethodAndArgsCallerのrunメソッドがmainメソッドを呼び出す。
 throw new ZygoteInit.MethodAndArgsCaller(m, argv);
}

MethodAndArgsCallerの定義は以下の通りで、ここではrunメソッドにフォーカスしていますが、runメソッドの実装はmainメソッドを呼び出すだけと非常にシンプルです。つまり、SystemServerのメインメソッドを呼び出して、SystemServerプロセスの作成と起動を完了します。

public static class MethodAndArgsCaller extends Exception
 implements Runnable {
 /** method to call */
 private final Method mMethod;//呼び出すメソッドは、ここではmainメソッドである。
 /** argument array */
 private final String[] mArgs;// 
 
 public void run() {
 try {
 mMethod.invoke(null, new Object[] { mArgs });
 }catch(){ } 
 }
}

申請プロセス作成開始プロセス

次のステップは、AMS の介入を必要とするアプリケーション・プロセスの作成と起動のプロセスを分析する ことですが、ここでは、プロセス全体の詳細にはあまり注意を払わずに、アプリケーション・プロセスの 作成と起動のプロセスに焦点を当てます。AMS に慣れている方であれば、Launcher をクリックしてアプリケーションを起動すると、AMS の startProcessLocked メソッドによってアプリケーションのプロセスが作成され、さらに startProcessLocked を呼び出してプロセスが完了することを知っているはずですので、startProcessLocked メソッドに注目してください。

frameworks/base/services/java/com/android/server/am/ActivityManagerService.java

private final void startProcessLocked(ProcessRecord app,
 String hostingType, String hostingNameStr) {
	 
	// Start the process. It will either succeed and return a result containing
	 // the PID of the new process, or else throw a RuntimeException.
 Process.ProcessStartResult startResult = Process.start("android.app.ActivityThread",
 app.processName, uid, uid, gids, debugFlags, mountExternal,
 app.info.targetSdkVersion, app.info.seinfo, null);
	 
}

このメソッドは、プロセスの作成を完了するには、プロセス開始メソッドクラスを介してされ、ここでActivityThreadクラス名を渡すために、アプリケーションプロセスの作成後に起動プロセスを完了するには、その内部のメインメソッドを呼び出す必要があります。プロセスがどのように作成されるかを見ていきます。

frameworks/base/core/java/android/os/Process.java

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[] zygoteArgs) {
 try {
 return startViaZygote(processClass, niceName, uid, gid, gids,debugFlags, mountExternal, targetSdkVersion, seInfo, zygoteArgs);
 } catch (ZygoteStartFailedEx ex) { }
}

startViaZygoteは、その名前から、アプリケーションプロセスがzygoteによって作成されることを意味します。startViaZygoteは、これらのパラメータをArrayList変数にパックし、zygoteSendArgsAndGetResultを呼び出してパックしたパラメータを渡します。このメソッドは、これらのパラメータをソケット経由でzygoteに渡す役割を果たします。

private static ProcessStartResult zygoteSendArgsAndGetResult(ArrayList<String> args)
 throws ZygoteStartFailedEx {
 openZygoteSocketIfNeeded();
 try {
 sZygoteWriter.write(Integer.toString(args.size()));
 sZygoteWriter.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");
 }
 sZygoteWriter.write(arg);
 sZygoteWriter.newLine();
 }
 sZygoteWriter.flush();
 // Should there be a timeout on this?
 ProcessStartResult result = new ProcessStartResult();
 result.pid = sZygoteInputStream.readInt();
  
 } catch (IOException ex) {
 }
}

zygote側では、zygoteが起動するとまずLocalSocketServerサービスに登録し、次にrunSelectLoopを介してAMS作成プロセスからのリクエストを待ちます。ここでrunSelectLoopメソッドを見る必要があります。

private static void runSelectLoop() throws MethodAndArgsCaller {
 ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
 ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
 FileDescriptor[] fdArray = new FileDescriptor[4];
  
 while (true) {
 int index;
  
 if (index < 0) {
 throw new RuntimeException("Error in select()");
 } else if (index == 0) {
 ZygoteConnection newPeer = acceptCommandPeer();
 peers.add(newPeer);
 fds.add(newPeer.getFileDesciptor());
 } else {
 boolean done;
 done = peers.get(index).runOnce();
 if (done) {
 peers.remove(index);
 fds.remove(index);
 }
 }
 }
}

runSelectLoopメソッドは、主にクライアント接続要求の処理とクライアントメッセージの処理を担当し、index=0は新しいクライアント接続があることを意味し、index>0は着信メッセージがあることを意味し、indexはクライアント接続インデックスを指定します。同時に、メッセージマシン型の処理でrunOnceを呼び出します。

//zygoteクライアントAMSから処理するメッセージを受け取る
boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
 String args[];
 Arguments parsedArgs = null;
  
 try {
 args = readArgumentList();//クライアントから送られたメッセージを読み、パラメータ・リストを取得する。
 descriptors = mSocket.getAncillaryFileDescriptors();
 } catch (IOException ex) { }
  
 try {
 parsedArgs = new Arguments(args);
  
 pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
 parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
 parsedArgs.niceName);//アプリケーション・プロセスの作成
 } catch () {}
  
 try {
 if (pid == 0) {
  
 //子プロセスを続けると、最終的にZygoteInitメソッドが投げられる。.MethodAndArgsCallerrunメソッドの呼び出しをトリガーするには
 handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
 // should never get here, the child is expected to either
 // throw ZygoteInit.MethodAndArgsCaller or exec().
 return true;
 } else { }
 } finally { }
}

このメソッドでは、まず readArgumentList メソッドがクライアントから送信されたメッセージを読み取り、引数のリストを取得します。次に、引数に基づいてアプリケーション・プロセスを作成し、さらに handleChildProc を呼び出して子プロセスの作成を完了します。このメソッドでは、SystemServer プロセスと同様に、MethodAndArgsCaller 例外がスローされ、run メソッドの呼び出しがトリガされます。

private void handleChildProc(Arguments parsedArgs,
 FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
 throws ZygoteInit.MethodAndArgsCaller {
  
 if (parsedArgs.invokeWith != null) {
 WrapperInit.execStandalone(parsedArgs.invokeWith,
 parsedArgs.classpath, className, mainArgs);
 } else {
  
 try {
 ZygoteInit.invokeStaticMain(cloader, className, mainArgs);//最終的に invokeStaticMain メソッドが呼び出され、例外がスローされる。
 } catch (RuntimeException ex) {
 logAndPrintError(newStderr, "Error starting.", ex);
 }
 }
}

このメソッドは、SystemServer のスタートアップで分析された invokeStaticMain を呼び出します。最終的には、ActivityThread の main メソッドを呼び出し、アプリケーションプロセスの起動を完了します。

Read next

トラッキング対プライバシーとセキュリティ、そのバランスはまだ問題である。

インターネットにおける個人のプライバシーの安全性は、インターネット分野が常に懸念している未解決の問題です。クッキーの追跡技術の出現は、インターネットのインテリジェンスを向上させましたが、利用者の個人のプライバシーの安全性を低下させました。

May 5, 2020 · 4 min read