Androidアプリケーションプロセスの作成プロセス

目次
  • 序文
  • ステップ
    • step1AmsがstartProcessLockedへのリクエストを開始します
    • step2Zygoteがリクエストを受け取りました
    • step3handleChildProc-子プロセスの世界に入ります
    • step4RuntimeInit.zygoteInit-子プロセス環境の準備
      • nativeZygoteInit
      • applicationInit
    • step5ActivityThread-アプリへの入り口
  • 総括する

序文

オリエンタルヘルスネットワークhttps://www.9559.org/cn/

startActivityの場合、Activity関連のエンティティの作成に加えて、システムは必要に応じてプロセスを作成します。このプロセスはLinuxレベルのプロセスエンティティを指します。後で説明するように、作成プロセスは実際にはフォークです。これは、親プロセスから新しいプロセスをコピーすることを意味します。このプロセスでは、イニシエーター、ams、zygoteなどの間のプロセス間通信を経験する必要があります。この記事では、主に、仮想マシンの継承、バインダースレッドプール、メッセージループの確立方法など、このプロセスにおけるamsの重要なリンクを整理します。など、およびスタートアップの詳細のアクティビティについては、他の章で説明します。

ステップ

step1AmsがstartProcessLockedへのリクエストを開始します

ActivityManagerService.java

Amsのzygoteへのリクエストの入り口から直接分析を開始します。もちろん、実際の開始はAmsが他のアプリケーションからバインダーリクエストを受信することなので、実際の開始であるonTransactにある必要があります。後でトレースを入力します。(ここに小さな詳細があります。ソースコードにLockedサフィックスが付いたメソッドがたくさんあります。これは、このメソッドを使用するとスレッドセーフではないため、ロックが必要であることを示しています)。

            // Start the process.  It will either succeed and return a result containing
            // the PID of the new process, or else throw a RuntimeException.
            boolean isActivityProcess = (entryPoint == null);
            if (entryPoint == null) entryPoint = "android.app.ActivityThread";
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
                    app.processName);
            checkTime(startTime, "startProcess: asking zygote to start proc");
            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);
            checkTime(startTime, "startProcess: returned from zygote!");
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

さらに重要なことは、この段落がProcessクラスのstartメソッドを呼び出し、エントリクラスをActivityThreadとして指定することです。

system    671   641   2348412 179992          0 0000000000 S Binder:641_1

641-671/system_process W:     at android.os.Process.zygoteSendArgsAndGetResult(Process.java:605)
641-671/system_process W:     at android.os.Process.startViaZygote(Process.java:738)
641-671/system_process W:     at android.os.Process.start(Process.java:521)
641-671/system_process W:     at com.android.server.am.ActivityManagerService.startProcessLocked(ActivityManagerService.java:4214)
641-671/system_process W:     at com.android.server.am.ActivityManagerService.startProcessLocked(ActivityManagerService.java:4069)
641-671/system_process W:     at com.android.server.am.ActivityManagerService.startProcessLocked(ActivityManagerService.java:3909)
641-671/system_process W:     at com.android.server.am.ActivityStackSupervisor.startSpecificActivityLocked(ActivityStackSupervisor.java:1441)
641-671/system_process W:     at com.android.server.am.ActivityStack.resumeTopActivityInnerLocked(ActivityStack.java:2741)
641-671/system_process W:     at com.android.server.am.ActivityStack.resumeTopActivityUncheckedLocked(ActivityStack.java:2213)
641-671/system_process W:     at com.android.server.am.ActivityStackSupervisor.resumeFocusedStackTopActivityLocked(ActivityStackSupervisor.java:1859)
641-671/system_process W:     at com.android.server.am.ActivityStack.completePauseLocked(ActivityStack.java:1393)
641-671/system_process W:     at com.android.server.am.ActivityStack.activityPausedLocked(ActivityStack.java:1237)
641-671/system_process W:     at com.android.server.am.ActivityManagerService.activityPaused(ActivityManagerService.java:7393)
641-671/system_process W:     at android.app.ActivityManagerNative.onTransact(ActivityManagerNative.java:571)
641-671/system_process W:     at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:3169)
641-671/system_process W:     at android.os.Binder.execTransact(Binder.java:565)

上記のトレースから、startActivityリクエストを受信した後、現在のアクティビティの一時停止操作が実行され、Process.javaクラスを介してZygoteプロセスへのリクエストが開始されていることが直接わかります。スレッド番号を観察すると、これらすべてがバインダースレッドで操作されていることがわかります。

AmsとZygoteは、通信関連のタスクを実行するためにzygoteStateクラスをカプセル化するソケットを介して接続されていることに注意してください。下の図のfdの占有率から、接合子プロセスがバインダーを使用していないことがはっきりとわかります。

device:/proc/341/fd # ls -l
ls -l
total 0
lrwx------ 1 root root 64 2021-03-16 17:49 0 -> /dev/null
lrwx------ 1 root root 64 2021-03-16 17:49 1 -> /dev/null
lr-x------ 1 root root 64 2021-03-16 17:49 10 -> /system/framework/core-oj.jar
lr-x------ 1 root root 64 2021-03-16 17:49 11 -> /system/framework/core-libart.jar
lr-x------ 1 root root 64 2021-03-16 17:49 12 -> /system/framework/conscrypt.jar
lr-x------ 1 root root 64 2021-03-16 17:49 13 -> /system/framework/okhttp.jar
lr-x------ 1 root root 64 2021-03-16 17:49 14 -> /system/framework/core-junit.jar
lr-x------ 1 root root 64 2021-03-16 17:49 15 -> /system/framework/bouncycastle.jar
lr-x------ 1 root root 64 2021-03-16 17:49 16 -> /system/framework/ext.jar
lr-x------ 1 root root 64 2021-03-16 17:49 17 -> /system/framework/framework.jar
lr-x------ 1 root root 64 2021-03-16 17:49 18 -> /system/framework/telephony-common.jar
lr-x------ 1 root root 64 2021-03-16 17:49 19 -> /system/framework/voip-common.jar
lrwx------ 1 root root 64 2021-03-16 17:49 2 -> /dev/null
lr-x------ 1 root root 64 2021-03-16 17:49 20 -> /system/framework/ims-common.jar
lr-x------ 1 root root 64 2021-03-16 17:49 21 -> /system/framework/apache-xml.jar
lr-x------ 1 root root 64 2021-03-16 17:49 22 -> /system/framework/org.apache.http.legacy.boot.jar
lrwx------ 1 root root 64 2021-03-16 17:49 23 -> socket:[9842]
lr-x------ 1 root root 64 2021-03-16 17:49 24 -> /system/framework/framework-res.apk
lr-x------ 1 root root 64 2021-03-16 17:49 25 -> /dev/urandom
l-wx------ 1 root root 64 2021-03-16 17:49 5 -> /sys/kernel/debug/tracing/trace_marker
lrwx------ 1 root root 64 2021-03-16 17:49 6 -> /dev/null
lrwx------ 1 root root 64 2021-03-16 17:49 7 -> /dev/null
lrwx------ 1 root root 64 2021-03-16 17:49 8 -> /dev/null
lrwx------ 1 root root 64 2021-03-16 17:49 9 -> socket:[9289]

Amsのコールスタックは、最上位に達すると終了し、Zygoteプロセスに入ります。

step2Zygoteがリクエストを受け取りました

Zygoteプロセスの初期化が完了した後、他のプロセスからの要求を待機する無限ループが存在し、コードを直接見つける必要があるとします。

 private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
        ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
        ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();

        fds.add(sServerSocket.getFileDescriptor());
        peers.add(null);

        while (true) {
            StructPollfd[] pollFds = new StructPollfd[fds.size()];
            for (int i = 0; i < pollFds.length; ++i) {
                pollFds[i] = new StructPollfd();
                pollFds[i].fd = fds.get(i);
                pollFds[i].events = (short) POLLIN;
            }
            try {
                Os.poll(pollFds, -1);
            } catch (ErrnoException ex) {
                throw new RuntimeException("poll failed", ex);
            }
            for (int i = pollFds.length - 1; i >= 0; --i) {
                if ((pollFds[i].revents & POLLIN) == 0) {
                    continue;
                }
                if (i == 0) {
                    ZygoteConnection newPeer = acceptCommandPeer(abiList);
                    peers.add(newPeer);
                    fds.add(newPeer.getFileDesciptor());
                } else {
                    boolean done = peers.get(i).runOnce();
                    if (done) {
                        peers.remove(i);
                        fds.remove(i);
                    }
                }
            }
        }
    }

ご覧のとおり、リクエストを受信すると、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);
    }
}

主な謎はforkAndSpecializeメソッドです。このメソッドは、ネイティブの子プロセスをフォークして呼び出し元に戻ることができますが、子プロセスと親プロセスの2回戻ります。これは、戻り値によって判断されます。 、およびLinuxフォークメソッドは似ています。子プロセスの場合は、handleChildProcを実行し、以下のコメントに注意してください。これは、上部のselectloopであるreturnステートメントが実行されないことを意味します(子プロセスは実行されないため) zygoteがコマンドループを受信することを心配する必要があり、アプリプロセスTaskを直接実行します);親プロセスはhandleParentProcを呼び出して戻ります。

子プロセスを返すには、例外をスローすることによって達成されるトリックがありますが、どこに戻ったのでしょうか。ZygoteInit.javaのメインメソッドを見てみましょう。

    public static void main(String argv[]) {
        // Mark zygote start. This ensures that thread creation will throw
        // an error.
        ...
        try {
            Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "ZygoteInit");
            RuntimeInit.enableDdms();
            // Start profiling the zygote initialization.
            SamplingProfilerIntegration.start();
            registerZygoteSocket(socketName);
            preload();
            gcAndFinalize();
            // Zygote process unmounts root storage spaces.
            Zygote.nativeUnmountStorageOnInit();
            ...
            Log.i(TAG, "Accepting command socket connections");
            /*这儿调用的runSelectLoop开启等待循环的*/
            runSelectLoop(abiList);
            
            closeServerSocket();
        } catch (MethodAndArgsCaller caller) {
            caller.run();
        } catch (Throwable ex) {
            Log.e(TAG, "Zygote died with exception", ex);
            closeServerSocket();
            throw ex;
        }
    }

おなじみの図runSelectLoopを見たので、ZygoteがJavaの世界に入った後、最初にmainメソッドに入って準備を行い、次にselectLoopを開いてループを待ち、子プロセスによってスローされたMethodAndArgsCaller例外を受け取った後、 caller.runを実行します。これは、呼び出しスタックから確認できる子プロセスの寿命の始まりです。これは、例外をスローし、いくつかのフォークや設定を削除することで、前の呼び出しスタックをクリアすることもできるためです。 、など。アプリの生活はより爽快なようです。onCreateへのフローは次の図のようになります。

3199-3199/com.android.myapplication W:     at com.android.myapplication.MainActivity.onCreate(MainActivity.java:42)
3199-3199/com.android.myapplication W:     at android.app.Activity.performCreate(Activity.java:6709)
3199-3199/com.android.myapplication W:     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118)
3199-3199/com.android.myapplication W:     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2624)
3199-3199/com.android.myapplication W:     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2732)
3199-3199/com.android.myapplication W:     at android.app.ActivityThread.-wrap12(ActivityThread.java)
3199-3199/com.android.myapplication W:     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1483)
3199-3199/com.android.myapplication W:     at android.os.Handler.dispatchMessage(Handler.java:102)
3199-3199/com.android.myapplication W:     at android.os.Looper.loop(Looper.java:154)
3199-3199/com.android.myapplication W:     at android.app.ActivityThread.main(ActivityThread.java:6141)
3199-3199/com.android.myapplication W:     at java.lang.reflect.Method.invoke(Native Method)
3199-3199/com.android.myapplication W:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:912)
3199-3199/com.android.myapplication W:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:802)

ハハ、私たちはそれから遠く離れています、子プロセスによって呼び出されたhandleChildProcが何をしたかを見ていきましょう。

step3handleChildProc-子プロセスの世界に入ります

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

慣例により、最初にコールスタックを確認します。

private void handleChildProc(Arguments parsedArgs,
        FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
        throws ZygoteInit.MethodAndArgsCaller {
        
        closeSocket();
        ZygoteInit.closeServerSocket();
    
        if (descriptors != null) {
            try {
                Os.dup2(descriptors[0], STDIN_FILENO);
                Os.dup2(descriptors[1], STDOUT_FILENO);
                Os.dup2(descriptors[2], STDERR_FILENO);

                for (FileDescriptor fd: descriptors) {
                    IoUtils.closeQuietly(fd);
                }
                newStderr = System.err;
            } catch (ErrnoException ex) {
                Log.e(TAG, "Error reopening stdio", ex);
            }
        }
     ......省略代码

     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 {
       ......省略代码
    }
}

当初は、fdを占有しないように、子プロセスを使用しなかったため、ソケットを閉じるなどのリソース回復作業がありました。dup2の3つの操作は、元の標準入力、標準出力、およびエラー出力(fdはそれぞれ0、1、および2)を、パラメーターによって渡される記述子に置き換えることです。実際、渡されるのは「/ dev / null」です。これは自由に入力できますアプリのfdは、0、1、および2が/ dev / nullでなければならないことを認識できます。次に、Amsによって渡されるパラメーターには「--runtime-init」があり、invokeWithはActivityTheadであり、RuntimeInit.zygoteInitメソッドを実行します。

step4RuntimeInit.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");

        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "RuntimeInit");
        redirectLogStreams();
        /*设置时区、log、http代理等一些信息*/
        commonInit();
        nativeZygoteInit();
        applicationInit(targetSdkVersion, argv, classLoader);
    }

CommonInitは一般的な初期化であり、深く掘り下げる必要はありません。nativeZygoteInit(より重要なメソッドは一般的にネイティブで実行されます)とapplicationInitに焦点を当てましょう。

nativeZygoteInit

要するに、一連の継承と書き換えの手法を通じて、最終的にapp_main.cpp.frameworks / base / cmds / app_process / app_main.cppに呼び出されました

virtual void onZygoteInit()
    {
        sp<ProcessState> proc = ProcessState::self();
        ALOGV("App process: starting thread pool.
");
        proc->startThreadPool();
    }

ネイティブバインダープロセスの記述に精通している場合は、これら2つの文に精通している必要があり、馴染みのあるルーチンを見てきました。これらの2つの文は、アプリが起動する前に/ dev /バインダーを開くのに役立ち、同時にバインダースレッドプールを開始しました。アプリがバインダーを使用した後は、バインダードライバーとスレッドプールに関連する問題を気にする必要はありません。

applicationInit

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);
}

プログラムは比較的単純です。メインメソッドは、リフレクションによってActivityThreadのメインメソッドを見つけ、例外をスローして、メソッドをzygoteのメインメソッドにパラメーターとして渡すことです(zygoteのメインメソッドは、開くことに加えて前述しました) selectLoopですが、MethodAndArgsCallerの例外もキャッチしました)。

    /**
     * Helper exception class which holds a method and arguments and
     * can call them. This is used as part of a trampoline to get rid of
     * the initial process setup stack frames.
     */
    public static class MethodAndArgsCaller extends Exception
            implements Runnable {
        /** method to call */
        private final Method mMethod;

        /** argument array */
        private final String[] mArgs;

        public MethodAndArgsCaller(Method method, String[] args) {
            mMethod = method;
            mArgs = args;
        }

        public void run() {
            try {
                mMethod.invoke(null, new Object[] { mArgs });
            } catch (IllegalAccessException ex) {
                throw new RuntimeException(ex);
            } catch (InvocationTargetException ex) {
                Throwable cause = ex.getCause();
                if (cause instanceof RuntimeException) {
                    throw (RuntimeException) cause;
                } else if (cause instanceof Error) {
                    throw (Error) cause;
                }
                throw new RuntimeException(ex);
            }
        }
    }

MethodAndArgsCallerのrunメソッドは非常に単純です。実際、ActivityThreadのメインメソッドを実行することです。さて、見てみましょう。zygoteは、子プロセスをフォークし、バインダースレッドプールの準備などを行い、最後にActivityThreadのメインメソッドを実行するために、このような大きな円を避けました。
コールスタックを介して子プロセスによって生命を作成するプロセスを要約します

1896-1896/com.android.myapplication W:     at com.android.internal.os.RuntimeInit.invokeStaticMain(RuntimeInit.java:237)
1896-1896/com.android.myapplication W:     at com.android.internal.os.RuntimeInit.applicationInit(RuntimeInit.java:338)
1896-1896/com.android.myapplication W:     at com.android.internal.os.RuntimeInit.zygoteInit(RuntimeInit.java:290)
1896-1896/com.android.myapplication W:     at com.android.internal.os.ZygoteConnection.handleChildProc(ZygoteConnection.java:757)
1896-1896/com.android.myapplication W:     at com.android.internal.os.ZygoteConnection.runOnce(ZygoteConnection.java:243)
1896-1896/com.android.myapplication W:     at com.android.internal.os.ZygoteInit.runSelectLoop(ZygoteInit.java:876)
1896-1896/com.android.myapplication W:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:798)

pidは子プロセスのpid番号ですが、親プロセスの呼び出しスタックを継承することに注意してください。handleChildProcから、子プロセスであり、以前は親プロセスzygoteでした。

step5ActivityThread-アプリへの入り口

.frameworks / base / core / java / android / app / ActivityThread.java

今回は主にActivityThreadの静的mainメソッドに注目します。これは、ファイル全体に6000行を超える行があり、これはさまざまなライフサイクルとActivityインスタンスの管理、およびviewrootの作成などに関連しているためです。

    public static void main(String[] args) {
        ...
        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

主にLooperの静的メソッドを呼び出してメッセージループを作成し、次にActivityThreadインスタンスを作成して、最後にメッセージループに入ります。

総括する

この記事では、Amsからアプリプロセスを作成する一般的なプロセスを大まかに要約します。実行プロセスエンティティの観点から、実際には3つの部分に分かれています。1つはamsにあり、もう1つはzygoteの親プロセスにあります。もう1つは子プロセスです。この一般的なコンテキストでは、コールスタックによると、実際には非常に明確です。amsは、バインダー要求の受信とzygoteへの接続を担当します。zygote親プロセスがソケットメッセージを受信すると、子プロセスをフォークします。子プロセスは、バインダー環境とメッセージループの準備を担当し、アクティビティのライフサイクルを開始します。

おすすめ

転載: blog.csdn.net/nidongla/article/details/115134389