Androidシステム起動(2) - Zygoteプロセス起動処理

1。概要

Androidシステム内では、 DVMDalvik仮想マシン)やARTシステムのサービスプロセスsystem_server 、アプリケーションプロセスはすべてZygoteプロセスによって作成されます(また、プロセスによって開発されるNativeプログラム、つまりC/C++プログラムもinitプロセスによって作成され、起動されます)。Zygoteプロセスはインキュベーターとも呼ばれ、fork(复制进程)アプリケーションプロセスとsystem_serverプロセスは の形で作成されます。プロセスはまたはZygoteを開始するときに作成するためアプリケーションプロセスと渡して作成されたプロセスは内部的にまたは のインスタンスコピーを取得できますDVMARTforksystem_serverDVMART

Zygoteプロセスは、initプロセスの開始時に作成されます。最初のZygoteプロセスの名前は ではなくZygote、 で定義されたapp_process名前です/frameworks/base/cmds/app_process/Android.mkZygoteプロセスが開始されると、Linuxシステム配下のシステムpctrlは と呼ばれapp_process、その名前は に変更されますZygote

ZygoteモデルですC/SZygoteプロセスはサーバーとして、Socketメソッドを通じて他のプロセスと通信します。ここでの「他のプロセス」とは主にシステム サービス プロセスを指しますsystem_server

Linuxシステムではfork、関数を呼び出して子プロセスを作成する場合、親プロセスのメモリはコピーされませんが、親プロセスと子プロセスはメモリ空間を共有しており、メモリコピーは子プロセスまたは子プロセスが実行されたときにのみ実行されます。親プロセスはメモリ データを変更するため、親プロセスと子プロセスは独自のメモリ空間を持ちます。それまでは、読み取り専用としてのみ共有されます。

コピーオンライト ( copy-on-write): データが変更されるまでメモリ空間は実際には割り当てられません。これは、データのコピーを遅らせたり、回避したりする可能性がある手法です。これはプログラムのパフォーマンスの最適化であり、不必要なコピーを避けることが目的です。

Zygoteインキュベーターとして、一部のリソースを事前にロードできるため、fork生成された子プロセスは再ロードせずにこれらのリソースを直接使用できます。たとえば、system_serverプロセスは、Zygoteプロセス内の関数、共有ライブラリ、共通クラス、テーマ リソースを直接使用できますJNI

Zygoteプロセスの起動では次のことが行われます。

  • メソッドを呼び出しAppRuntime/AndroidRuntime.startJava仮想マシンを作成し、JNIそのためのメソッドを登録します。
  • JNIメソッドを呼び出して、レイヤーZygoteInit.mainからフレームワークレイヤーNativeに入りますJava
  • プロセスを開始しますsystem_server
  • ZygoteServerサーバー側でリクエストを作成しSocketZygoteServer.runSelectLoopメソッドを通じてリクエストを待機することによりAMS、新しいアプリケーション プロセスを作成します。

以下にZygote起動時のタイミング図を示します。

Zygot の起動プロセス

2Zygote起動スクリプト

init プロセスを開始するプロセスでは、initプロセスがファイルを解析し、フィールドで指定されたプロセスをinit.rc作成してロードすることが記載されており、プロセスはこの方法でプロセスによってロードされます。ファイル内の type ステートメントを使用して、初期化言語 ( ) によって記述された起動スクリプト導入します。serviceZygoteinitinit.rcImportZygoteAndroidAndroid Init Language

// /system/core/rootdir/init.rc
import /init.environ.rc
import /init.usb.rc
import /init.${
    
    ro.hardware}.rc
import /vendor/etc/init/hw/init.${
    
    ro.hardware}.rc
import /init.usb.configfs.rc
import /init.${
    
    ro.zygote}.rc  // ${ro.zygote} 由厂商定义,与平台无关

init.rc固定のファイルが直接インポートされるのではなく、ro.zygote内容に応じて異なるファイルがインポートされることがわかります。

からは1 ビット プログラムのAndroid 5.0サポートを開始したため、 1 ビットと 1 ビットには違いがあるため、属性を使用して異なる起動スクリプトの使用を制御、異なるバージョンのプロセスが開始されるようにします。異なるバージョンのプロセスが開始されます。次の値があります64Zygote3264ro.zygoteZygoteZygoteZygotero.zygote4

  • init.zygote32.rc:Zygoteプロセスに対応する実行プログラムはapp_process(ピュア32 bitモード) です。
  • init.zygote32_64.rc: 2 つのZygoteプロセス (zygotezygote_secondary) を開始し、対応する実行プログラムはapp_process32(メイン モード)、app_process64;
  • init.zygote64.rc:Zygoteプロセスに対応する実行プログラムはapp_process64(ピュア64 bitモード) です。
  • init.zygote64_32.rc: 2 つのZygoteプロセス (zygotezygote_secondary) を開始し、対応する実行プログラムはapp_process64(メイン モード)、app_process32;

これらのZygote起動スクリプトはsystem/core/rootdir次のディレクトリに配置されます。

ro.zygotes プロパティ

64ビットプロセッサを例にとると、init.zygote64_32.rcコードは次のようになります。

init.zygote64_32

スクリプトには 2 つの type ステートメントがあり、2 つのプロセスが開始されることServiceを示しています。1 つはメイン モードとして という名前で実行プログラムは であり、もう 1 つはセカンダリ モードとして という名前で実行プログラムは です。他のスクリプト ファイルも同様です。Zygotezygoteapp_process64zygote_secondaryapp_process32

たとえばinit.zygote64_32.rc、起動ファイルのアドレスは/system/bin/app_process64(上図) で、app_process64対応するコードは/frameworks/base/cmds/app_processのディレクトリにあります。

まず、以下を見てくださいAndroid.mk

// /frameworks/base/cmds/app_process/Android.mk
LOCAL_PATH:= $(call my-dir)
....
app_process_src_files := \
     app_main.cpp \
...
LOCAL_MODULE:= app_process
LOCAL_MULTILIB := both
LOCAL_MODULE_STEM_32 := app_process32
LOCAL_MODULE_STEM_64 := app_process64
...

上記のコードからわかるように、 または のいずれであってもapp_prcessapp_prcess32対応app_prcess64するソース ファイルは ですapp_main.cpp

3Zygoteプロセス起動プロセス

initZygoteプロセスは主にapp_main.cppファイル内の関数を呼び出すことによって開始されmain、その中でプロセスは次のメソッドAppRuntimeを呼び出すことによってstart開始されますZygote

// /frameworks/base/cmds/app_process/app_main.cpp
int main(int argc, char* const argv[])
{
    
    
   ...
    while (i < argc) {
    
    
        const char* arg = argv[i++];
        if (strcmp(arg, "--zygote") == 0) {
    
     // 1
            // 如果当前运行在Zygote进程中,则将zygote设置为true
            zygote = true; // 2
            niceName = ZYGOTE_NICE_NAME;
        } else if (strcmp(arg, "--start-system-server") == 0) {
    
     // 3
            // 如果当前运行在system_server进程中,则将startSystemServer设置为true
            startSystemServer = true; // 4
        } 
        ...
    }
    ...
    if (!niceName.isEmpty()) {
    
    
        runtime.setArgv0(niceName.string(), true /* setProcName */);
    }
	// 如果运行在zygote进程中
    if (zygote) {
    
     // 5
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote); // 6
    } else if (className) {
    
    
        runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
    } else {
    
    
        fprintf(stderr, "Error: no class name or --zygote supplied.\n");
        app_usage();
        LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
    }
}
 AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));

Zygoteプロセスはfork自らを介してサブプロセスを作成し、Zygoteプロセスとそのサブプロセスの両方が関数app_main.cppmain関数に入ることができるようにするためmain、関数内で現在どのプロセスが実行されているかを区別するために、関数にパラメータが含まれているかどうかを1判断します。コメント が含まれている場合は、関数がプロセス内で実行されていることを意味し、コメントはに設定されます同様に、コメントにパラメータが含まれているかどうかを判断し、含まれている場合は関数試行中であることを意味し、コメントを設定しますarg--zygotemainZygote2zygotetrue3arg--start-system-servermainsystem_server4startSystemServertrue

コメントで がであれ5、プロセス内で現在実行中であることを意味し、コメントの関数が呼び出されますから継承され、メソッドをオーバーライドしなかったので、に入力されたプロセスのコードは次のとおりです。zygotetruezygote6AppRuntimestartAppRuntimeAndroidRuntimestartZygoteAndroidRutime

// /frameworks/base/core/jni/AndroidRuntime.cpp
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
    
    
    ...
    /* start the virtual machine 启动Java虚拟机 */
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;
    if (startVm(&mJavaVM, &env, zygote) != 0) {
    
     // 1
        return;
    }
    onVmCreated(env);

    /*
     * Register android functions. 为Java虚拟机注册JNI方法
     */
    if (startReg(env) < 0) {
    
     // 2
        ALOGE("Unable to register all android natives\n");
        return;
    }

    ...
    // 从app_main的main函数得知className为com.android.internal.os.ZygoteInit
    classNameStr = env->NewStringUTF(className); // 3
    assert(classNameStr != NULL);
    env->SetObjectArrayElement(strArray, 0, classNameStr);

    for (size_t i = 0; i < options.size(); ++i) {
    
    
        jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());
        assert(optionsStr != NULL);
        env->SetObjectArrayElement(strArray, i + 1, optionsStr);
    }

    /*
     * Start VM.  This thread becomes the main thread of the VM, and will
     * not return until the VM exits.
     * 将className的"."替换为"/"
     */
    char* slashClassName = toSlashClassName(className != NULL ? className : ""); // 4
    // 找到 zygoteInit
    jclass startClass = env->FindClass(slashClassName); // 5
    if (startClass == NULL) {
    
    
        ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
        /* keep going */
    } else {
    
    
        // 找到ZygoteInit的main函数
        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
                                                     "([Ljava/lang/String;)V"); // 6
        if (startMeth == NULL) {
    
    
            ALOGE("JavaVM unable to find main() in '%s'\n", className);
            /* keep going */
        } else {
    
    
            // 通过JNI调用ZygoteInit的main方法
            env->CallStaticVoidMethod(startClass, startMeth, strArray); // 7

            #if 0
            if (env->ExceptionCheck())
                threadExitUncaughtException(env);
            #endif
        }
    }
    free(slashClassName);

    ALOGD("Shutting down VM\n");
    if (mJavaVM->DetachCurrentThread() != JNI_OK)
        ALOGW("Warning: unable to detach main thread\n");
    if (mJavaVM->DestroyJavaVM() != 0)
        ALOGW("Warning: VM did not shut down cleanly\n");
}

コメント1内の関数を呼び出して仮想マシンstartVmを作成しJavaコメント内の関数2を呼び出して仮想マシンを登録します注釈内のの値は渡されるパラメータであり、その値は ですコメントの関数を使用して に置換し、置換後の値を に代入し、 に従ってコメント位置検索し、 を見つけた後にコメントのメソッドを呼び出します最後に、コメントでメソッドが呼び出されますなぜここを使うのかメソッドは言語で記述されているため、現在実行中のロジックは にあり、 を介して呼び出す必要がありますこのようにして、レイヤーからフレームレイヤーに入りますstartRegJavaJNI3classNamecom.android.internal.os.ZygoteInit4toSlashClassNameclassName./com/android/internal/os/ZygoteInitslashClassName5slashClassNameZygoteInitZygoteInit6ZygoteInit.main7JNIZygoteInit.mainJNIZygoteInit.mainJavaNativeJNIJavaZygoteNativeJava

メソッドJNIを呼び出した後、フレームワーク層に入ります。その前に、コードはフレームワーク層に入りません。つまり、フレームワーク層が作成されましたメソッドのコードは次のとおりです:ZygoteInit.mainZygoteJavaJavaZygoteJavaZygoteInit.main

// /frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
public static void main(String argv[]) {
    
    
    // ZygoteServer是Zygote进程的Socket通讯服务端的管理类
    ZygoteServer zygoteServer = null;
    ...
    Runnable caller;
    try {
    
    
        if (!enableLazyPreload) {
    
    
            bootTimingsTraceLog.traceBegin("ZygotePreload");
            EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
                                SystemClock.uptimeMillis());
            preload(bootTimingsTraceLog); // 1
            EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
                                SystemClock.uptimeMillis());
            bootTimingsTraceLog.traceEnd(); // ZygotePreload
        } else {
    
    
            Zygote.resetNicePriority();
        }
        
        ...
        zygoteServer = new ZygoteServer(isPrimaryZygote); // 2
		
        if (startSystemServer) {
    
    
            Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer); // 3
            // {@code r == null} in the parent (zygote) process, and {@code r != null} in the
            // child (system_server) process.
            if (r != null) {
    
    
                r.run();
                return;
            }
        }

        Log.i(TAG, "Accepting command socket connections");

        // The select loop returns early in the child process after a fork and
        // loops forever in the zygote.
        caller = zygoteServer.runSelectLoop(abiList); // 4
    } catch (Throwable ex) {
    
    
        Log.e(TAG, "System zygote died with exception", ex);
        throw ex;
    } finally {
    
    
        if (zygoteServer != null) {
    
    
            zygoteServer.closeServerSocket();
        }
    }
	...
}

コメントは1クラスとリソースをプリロードします。コメントにより、新しいアプリケーション プロセスの作成要求に応答するチャネルがエンドに2作成されます。コメントでプロセスを開始すると、システム サービスもプロセスによって開始されます。このメソッドはコメント時に呼び出され、新しいプロセスを作成するリクエストを待ちます。ServerSocketActivityManagerServiceZygote3system_serversystem_server4ZygoteServer.runSelectLoopAMS

上記から、このメソッドは主に 2 つのことを行うことがわかりますZygoteInit.main4

  • クラスとリソースをプリロードします
  • 作成Server側は、新しいアプリケーション プロセスを作成する要求Socketに応答するために使用されます。ActivityManagerService
  • system_serverプロセスを開始すると、システム サービスもsystem_server開始されます。
  • ActivityManagerService新しいアプリケーションプロセスを作成するリクエストを待っています。

参考

【Android 10システム開始】シリーズ – Zygote(プロセスインキュベーター)

おすすめ

転載: blog.csdn.net/xingyu19911016/article/details/127686947