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の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接続の確立は、最終的にZygoteInitZygoteConnectionオブジェクトを作成し、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端に返して、この時ActivityManagerServicestartProcessLockedは実行を続けることができます.サブプロセスが実行するコードを見てみましょう.最終的には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)の異常を放出し,この異常はActivityThreadmainの方法を反射する.前節ZygoteInitmainの方法を覚えていますか?
  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プロセス解析についてはここまで