Android P Zygotプロセスのソースコード分析

init プロセスはユーザー空間の最初のプロセスであり、zygote プロセスは最初の Java プロセスです。zygote プロセスは init プロセスの子プロセスであり、init プロセスは rc ファイルを解析することで zygote プロセスを実行します。

Zygote は Android システムの非常に重要なプロセスであり、その主な機能は Android アプリケーションを実行することです。Android システムでの新しいアプリケーションの実行 (卵子の受精や分割など) は、Zygote プロセス (仮想マシンなど、アプリケーションの実行に必要なさまざまな要素と条件があります) と組み合わせる必要があります。実行します。

Zygote プロセス zygote
プロセスが実行中に、Art (または Dalvik) 仮想マシンが初期化されて起動されます。Android アプリケーションは Java で書かれており、ローカル プロセスの形式で Linux 上で直接実行することはできず、仮想マシン内でのみ実行できます。また、各アプリケーションプログラムはそれぞれ独自の仮想マシン上で動作するため、アプリケーションプログラムを実行するたびに仮想マシンを再初期化して起動する必要がある場合、その処理に非常に時間がかかります。したがって、Android では、アプリケーション プログラムが実行される前に、zygote プロセスが実行中の仮想マシンのコードとメモリ情報を共有することで、アプリケーション プログラムの実行にかかる時間を短縮します。また、アプリケーションが利用するAndroid Frameworkのクラスやリソースをあらかじめメモリ上にロードし、利用するリソースのリンク情報を整理・形成します。新しく実行する Android アプリケーションが必要なリソースを使用する場合、リソースのリンク情報を毎回再生成する必要がないため、時間を大幅に節約し、プログラムの実行速度が向上します。

では、Linux システムでプロセスを作成して実行することと、Android システムで zygote を介してアプリケーションを作成して実行することの違いは何でしょうか?

次の図はすべて、「Uncovering the Android Framework」という本から引用しています (実際には自分で 0.0 を描きたくありません)。


上の図に示すように、親プロセス A は fork() 関数を呼び出して、新しい子プロセス A' を作成します。新しく作成されたプロセスA'は、親プロセスのメモリ構造情報やライブラリ接続情報を共有します(COWコピーオンライト技術)。次に、子プロセス A' は exec('B') を呼び出して、新しいプロセス B のコードをメモリにロードします。この時点で、ロードされた B プログラムを実行するためにメモリが再割り当てされ (以前に共有されていたため)、B プロセスが単独で使用するための新しいライブラリ接続情報が形成されます。


上の図に示すように、zygote プロセスは fork() 関数を呼び出して zygote' 子プロセスを作成し、子プロセス zygote' は親プロセス zygote のコード領域とリンク情報を共有します。新しい Android アプリケーション A は、exec() を通じて既存のプロセスのコード領域を再ロードするのではなく、コピーされた仮想マシン (仮想空間のコピー、物理空間は同じです) に動的にロードすることに注意してください。すると、zygote'プロセスはアプリケーションクラスAのメソッドに実行処理を引き渡し、Androidアプリケーションが動作を開始します。新しく生成されたアプリケーション A は、既存の zygote プロセスのライブラリとリソースのリンク情報を使用するため、非常に高速に動作します。


上の図に示すように、zygote が起動すると、art (または dalvik) 仮想マシンを初期化して実行し、必要なクラスとリソースをメモリにロードします。次に、fork() を呼び出して zygote の子プロセスを作成し、zygote の子プロセスが Android アプリケーション A を動的にロードして実行します。実行中のアプリケーション A は、zygote が初期化して実行を開始した仮想マシン コードを使用し、メモリに記録されているクラスとリソースを使用して動作を高速化します。

COW (Copy on write)
新しいプロセスを作成した後、新しいプロセスは親プロセスのメモリ空間を共有します。つまり、新しい子プロセスは親プロセスのメモリ空間に関連するすべての情報をコピーして使用します。COW はメモリ複製技術です。一般にメモリコピーのオーバーヘッドは非常に大きいため、作成した子プロセスが親プロセスのメモリ空間を参照する場合は、コピーせずに直接親プロセスのメモリ空間を共有するようにしてください。共有メモリ上の情報を変更する必要がある場合、子プロセスは親プロセス内の該当メモリ情報を自身のメモリ空間にコピーして変更するCOW(Copy on write)技術を使用します。

fork 後および実行前に、2 つのプロセスは同じ物理空間 (メモリ領域) を使用します。子プロセスのコード セグメント、データ セグメント、およびスタックはすべて、親プロセスの物理空間、つまり仮想プロセスを指します。両者の空間は異なりますが、対応する物理空間は同じです。

Zygote プロセスのソース コード分析
app_process ZygoteInit クラスによって実行
zygote は Java で書かれており、init プロセスによって直接起動して実行することはできません。zygote クラスを実行する場合は、まず仮想マシンを作成し、その仮想マシン上で ZygoteInit クラスを実行する必要があります。このタスクを実行するのは app_process プログラムです。
zygote プロセスの起動プロセスを分析してみましょう:
/system/core/rootdir/init.rc

import /init.$(ro.zygote).rc

64 ビット システムの場合、$(ro.zygote) の値は "zygote64"
/system/core/rootdir/init.zygote64.rc

サービス zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
    ...
    クラス メイン
    ユーザー ルート
    ソケット zygote ストリーム 660 ルート システム // /dev/socket/zygote ソケットを作成
    ..

init プロセスは、上記の init.zygote64.rc ファイルを解析した後、/system/bin/app_process64 プロセスを実行し、/dev/socket/zygote ソケットを生成します。ZygoteInit は、これを使用して AMS A リクエストを受信し、新しいソケットを作成します。応用。
init プロセスは rc ファイル プロセスを分析して実行します。以前の記事「Init プロセスの起動プロセス」を参照してください。

app_process64 プログラムのメイン関数エントリは次のとおりです。

フレームワーク/base/cmds/app_process/app_main.cpp

int main(int argc, char* const argv[])
{     ......     AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));     ......     // 実行時引数を解析します。最初の認識されないオプションで停止します。     ブール接合子 = false;     bool startSystemServer = false;     ……






    while (i < argc) {         const char* arg = argv[i++];         if (strcmp(arg, "--zygote") == 0) {             zygote = true;         else if (strcmp(arg, "--start-system-server") == 0) {             startSystemServer = true;         }          ……     }







    ......
    if (zygote) {         //zygote プロセス         runtime.start("com.android.internal.os.ZygoteInit", args, zygote);     } else if (className) {         // 通常の Java プロセス         ランタイム。 start("com.android.internal.os.RuntimeInit", args, zygote);     } } zygote プロセスの場合は ZygoteInit コードを実行し、通常の Java プロセスの場合は RuntimeInit コードを実行します。 am コマンドを使用した場合、/system/bin /am は実際にはシェル スクリプトであり、内部のコードを見ると、app_process を通じて通常の Java プロセスが起動され、AMS と通信していることがわかります。フレームワーク/base/core/jni/AndroidRuntime.cpp









//AppRuntime は AndroidRuntime を継承
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{     /* 仮想マシンを起動します */     JniInvocation jni_invocation;     // ① 指定された仮想マシンのライブラリをロードしますmachine (art または dalvik)     jni_invocation.Init(NULL);      JNIEnv* env;     // ② Java 仮想マシンを作成     if (startVm(&mJavaVM, &env, zygote) != 0) {         return;     }








    // ③ JNI 関数を登録
    if (startReg(env) < 0) {         return;     }

    ......
    // ④ 正式にJavaの世界に入り、ZygoteInit.javaのメインメソッドを呼び出す
    jclass startClass = env->FindClass(slashClassName);
    ......
    jmethodID startMeth = env->GetStaticMethodID(startClass, " main","([Ljava/lang/String;)V");
    ......
    env->CallStaticVoidMethod(startClass, startMeth, strArray);
    ......
}

①: のライブラリをロードします。仮想マシン (art または dalvik)、関数ポインタはライブラリに対応する関数 (例: JNI_CreateJavaVM 関数)
libnativehelper/JniInvocation.cppを指します。

bool JniInvocation::Init(const char* library) {   //ロードするライブラリの名前を取得   library = GetLibrary(library,buffer);

  //対応する仮想マシン ライブラリを動的にロードします
  handle_ = dlopen(library, RTLD_NOW);
  ......
  //JNI_CreateJavaVM_ 関数ポインタは、仮想マシン ライブラリ内の JNI_CreateJavaVM 関数を指します
  if (!FindSymbol(reinterpret_cast<void**> ( &JNI_CreateJavaVM_),
                  "JNI_CreateJavaVM")) {     return false;   }   ......   return true; } GetLibraray() 関数は、ダイナミック ライブラリ (libart.so または libdalvik .so) の名前を取得し、dlopen () 関数は仮想マシン ライブラリを動的にロードします。RTLD_NOW は、戻る直前にすべての未配置シンボルをリンクすることを意味し、FindSysmbol() 関数ポインタは対応する関数を指します。libnativehelper/JniInvocation.cpp







static const char* kLibrarySystemProperty = "persist.sys.dalvik.vm.lib.2";
static const char* kDebuggableSystemProperty = "ro.debuggable";
static const char* kDebuggableFallback = "0"; // デバッグ不可。
static const char* kLibraryFallback = "libart.so";

const char* JniInvocation::GetLibrary(const char* ライブラリ, char* バッファ) {     char デバッグ可能[PROPERTY_VALUE_MAX];     //「ro.debuggable」のプロパティプロパティ     property_get(kDebuggableSystemProperty, debuggable,kDebuggableFallback);


    if (strcmp(debuggable, "1") != 0) {         //デバッグデバイスでない場合、ライブラリの値は「libart.so」です         library = kLibraryFallback;     } else {         //デバッグデバイスの場合、ライブラリの値は「 Persisit.sys.dalvik.vm.lib.2 」プロパティ値         property_get(kLibrarySystemProperty,buffer,kLibraryFallback);     }





    return library;
}
: Java 仮想マシン
Frameworks/base/core/jni/AndroidRuntime.cppを作成します

int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote)
{     JavaVMInitArgs initArgs;     ......     initArgs.version = JNI_VERSION_1_4;     initArgs.options = mOptions.editArray();     initArgs.nOptions = mOptions.size();     initArgs.ignoreUnrecognized = JNI_FALSE;





    ......
    // Java 仮想マシンを作成する
    if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {         ALOGE("JNI_CreateJavaVM failed\n");         return -1;     }


    return 0;
}

JNI_CreateJavaVM() は JniInvocation.cpp 内の関数で、①で述べた JNI_CreateJavaVM_() 関数ポインタを呼び出し、最後に対応する仮想マシン動的ライブラリ内の JNI_CreateJavaVM() 関数を呼び出して、対応する仮想マシンを作成します。initArgs は、受信する仮想マシンのパラメータを表します。
③: JNI ローカル関数の登録
まずはいくつかのデータ構造について理解しましょう:
Frameworks/base/core/jni/AndroidRuntime.cpp

#define REG_JNI(名前) { 名前 }

struct RegJNIRec {     int (*mProc)(JNIEnv*); }; ...... static const RegJNIRec gRegJNI[] = {     REG_JNI(register_com_android_internal_os_RuntimeInit),     REG_JNI(register_com_android_internal_os_ZygoteInit_nativeZygoteInit),     REG_ JNI(register_android_os_SystemCロック)、     REG_JNI(register_android_util_EventLog)、     REG_JNI (register_android_util_Log),     ...... }; gRegJNI は RegJNIRec 構造体の配列であり、配列は REG_JNI マクロ定義によって初期化されます。たとえば、gRegJNI の最初の要素の初期化は次と同等です。












gRegJNI[0] = {register_com_android_internal_os_RuntimeInit};

次に、gRegJNI[0] 内の RegJNIRec 構造体の mProc 関数ポインタは、上記の関数 register_com_android_internal_os_RuntimeInit を指し、同じことが配列内の他の要素にも当てはまります。JNI ローカル関数の登録を分析してみましょう。
フレームワーク/base/core/jni/AndroidRuntime.cpp

int AndroidRuntime::startReg(JNIEnv* env)
{     ......     //JNI ローカル関数を登録し、gRegJNI 配列を     if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {         return -1;     }     ......     0 を返します; }







static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env)
{     //gRegJNI 配列内の要素の mProc 関数をトラバースして実行します     //(mProc は関数ポインタであり、配列はすでに配列の初期化時に指定された関数)     for (size_t i = 0; i < count; i++) {         if (array[i].mProc(env) < 0) {             return -1;         }     }     return 0; } startReg は呼び出しますregister_jni_procs は gRegJNI 配列内の mProc 関数を走査して呼び出します。例として最初の要素 gRegJNI[0].mProc(env) を取り上げます。 上記の分析から、 register_com_android_internal_os_RuntimeInit(env) という関数が実際に呼び出されていることがわかります。 。フレームワーク/base/core/jni/AndroidRuntime.cpp











    int     };         ......             (void*) com_android_internal_os_RuntimeInit_nativeFinishInit },         { "nativeFinishInit", "()V",     const JNINativeMethod Methods[] = {
{ register_com_android_internal_os_RuntimeInit(JNIEnv* env)     jniRegisterNativeMethods (env, "com/android/internal/os/RuntimeInit",         メソッド, NELEM(methods)); }










上記のコードからわかるように、nativeFinishInit 関数は com_android_internal_os_RuntimeInit_nativeFinishInit 関数をマップします。Java がnativeFinishInit 関数を呼び出すと、実際には c/c++ の com_android_internal_os_RuntimeInit_nativeFinishInit 関数が呼び出されます。Java が jni 関数を呼び出すと、仮想マシンは自動的にマッピングされるのに、なぜ自分でマッピングする必要があるのか​​と疑問に思う読者もいるかもしれません。jni 関数の数が少ない場合、これは確かに実現可能ですが、gRegJNI 配列は非常に大きく、マッピングする必要がある関数が多数あることがわかります。それらすべてがマッピングのために仮想マシンに割り当てられている場合、実行は仮想マシンのパフォーマンスが大幅に低下するため、JNI 関数の登録を進めます。これにより、仮想マシンは呼び出す対応する関数を直接見つけることができます。
④: ZygoteInit.java の main 関数をリフレクション経由で呼び出し、正式に Java の世界に入ります。env->FindClass は ZygoteInit クラスのタイプを取得し、env->GetStaticMethodID は関数 main の関数 ID を取得し、env->CallStaticVoidMethod は ZygoteInit.java の静的関数 main を呼び出します。

ZygoteInit クラスの機能
ここまでで、仮想マシンを作成し、ZygoteInit クラスを仮想マシンにロードしました。次に、ZygoteInit クラスが実行されますが、ZygoteInit クラスの具体的な機能は何でしょうか。それは大まかに次のように要約できます。

新しい Android アプリケーションの実行リクエストを受信するためにソケットをバインドする
Android アプリケーション フレームワークで使用されるクラスとリソースをプリロードする
SystemServer を起動して実行し、
新しい Android アプリケーションの実行リクエストを処理する
ZygoteInit の主な機能:
Frameworks/ Base/core/java/com /android/internal/os/ZygoteInit.java

public static void main(String argv[]) {     ZygoteServer zygoteServer = new ZygoteServer();     ......     try{         boolean startSystemServer = false;         Stringソケット名 = "zygote";//套接字默认名前zygote         for (int i = 1; i < argv.length; i++) {             if ("start-system-server".equals(argv[i])) {                 startSystemServer = true;            }             ……         }










        // ① /dev/socket/zygote ソケットをバインドして、Android アプリケーションの実行リクエストを受信します
        zygoteServer.registerServerSocketFromEnv(socketName);
        ......
          
        if (!enableLazyPreload) {             ......             // ② クラスとリソースをプリロードします             preload (bootTimingsTraceLog);             ......         }




        ……
        if (startSystemServer) {             // ③ fork出system_server子进程            Runnable r = forkSystemServer(abiList,ソケット名,zygoteServer);

            // ③ {@code r == null} は親プロセス (zygote) を意味し、子プロセス (system_server) の {@code r != null}
            if (r != null) {                 // ③ 子の場合プロセス (system_server ) は run() メソッドを実行して戻り、親プロセス (zygote) は引き続き                 r.run();                 return;             }         }




        Log.i(TAG, "コマンドソケット接続の受け入れ");

        // ④ このポーリングは zygote プロセス内で無限ループし、フォークされた子プロセス (Android アプリケーション プロセス) は終了します
        caller = zygoteServer.runSelectLoop(abiList);
    } catch (Throwable ex) {         Log.e(TAG, "System zygote例外で死亡しました", ex);         throw ex;     }finally {         //system_server プロセスと Android アプリケーション プロセスはソケットを閉じますが、zygote はまだ runSelectLoop でソケットをポーリングしリッスンしています         zygoteServer.closeServerSocket();     }






    

    // ④ Android アプリケーションのプロセスがここに来て、対応するコマンドを実行します
    if (caller != null) {         caller.run();     } } ①: Android アプリケーションの実行リクエストを受け取るソケットをバインドしますFrameworks/base /core /java/com/android/internal/os/ZygoteServer.java





void registerServerSocketFromEnv(StringソケットName) {     if (mServerSocket == null) {         int fileDesc;         //fullSocketNameは「ANDROID_SOCKET_zygote」         final String fullSocketName = ANDROID_SOCKET_PREFIX +ソケットName;         try {             //ANDROID_SOCKET_zygoteの環境変数を取得します(つまり、/dev/socket / zygote のファイル記述子の値)             //zygote プロセスの開始時に init プロセスによって環境変数に保存された文字列です             env = System.getenv(fullSocketName);             fileDesc = Integer.parseInt(env);         } catch (RuntimeException ex ) {             throw new RuntimeException(fullSocketName + "未設定または無効"、例);         }         try {












        

            //後で Android アプリケーションの起動リクエストを受信するために使用されるバインド ソケット
            FileDescriptor fd = new FileDescriptor();
            fd.setInt$(fileDesc);
            mServerSocket = new LocalServerSocket(fd);
            mCloseSocketFd = true;
        } catch (IOException ex) {             .. ....         } } }     ② : プリロードされたクラスとリソース、および zygote プロセスからフォークされたアプリケーション プロセスを直接共有することで、アプリケーション プロセスの起動を高速化できます。フレームワーク/base/core/java/com/android/internal/os/ZygoteInit.java






static void preload(TimingsTraceLog bootTimingsTraceLog) {     ......     preloadClasses();     ......     preloadResources();     ......     nativePreloadAppProcessHALs();     ......     preloadOpenGL();     preloadSharedLibraries( );     preloadTextResources();     ......     } preloadClasses、preloadResources、nativePreloadAppProcessHAL はクラスやリソースをプリロードします。興味のある学生はさらに詳しく学ぶことができます。③: forkSystemServer は、system_server 子プロセスをフォークし、SystemServer の main メソッドを呼び出すことができる Runnable r を返し、対応する run メソッドを実行します。一方、親プロセス zygote は runSelectLoop を実行し続け、Android アプリケーションの実行リクエストをリッスンします。フレームワーク/base/core/java/com/android/internal/os/ZygoteInit.java















private static Runnable forkSystemServer(String abiList, StringソケットName,ZygoteServer zygoteServer) {     ......     /* システムサーバーを起動するためのハードコードされたコマンドライン */     String args[] = {         "--setuid=1000", //user id         "--setgid=1000", //グループ ID         ......         "--nice-name=system_server",         ......         "com.android.server.SystemServer",     };     ZygoteConnection.Arguments parsedArgs = null;     int pid;     try {         parsedArgs = new ZygoteConnection.Arguments(args);         ......         //system_server 子プロセスをフォークし、対応するパラメータを設定します         pid = Zygote.forkSystemServer(










    







                parsedArgs.uid, parsedArgs.gid,
                parsedArgs.gids,
                parsedArgs.runtimeFlags,
                null,
                parsedArgs.permittedCapabilities,
                parsedArgs.EffectiveCapabilities);
    } catch (IllegalArgumentException ex) {         throw new RuntimeException(ex );     }     //子プロセス (system_server)     if (pid == 0) {         ......         //返された r は null ではないため、直接 r.run         return handleSystemServerProcess(parsedArgs);     }     // 親プロセス (zygote)、戻り値は null、次へ進む実行して     null を返す; }












ZygoteInit の forkSystemServer メソッドは Zygote の forkSystemServer メソッドを呼び出し、子プロセス (system_server) の場合は handleSystemServerProcess() を返し、親プロセス (zygote) は null を返します。
フレームワーク/base/core/java/com/android/internal/os/Zygote.java

public static int forkSystemServer(int uid, int gid, int[] gids, int runtimeFlags, int[][] rlimits,long allowedCapabilities, long activeCapabilities) { ...... int pid =nativeForkSystemServer(uid, gid,     gid     , runtimeFlags,                  rlimits, allowedCapabilities,popularCapabilities);     ......     return pid;     }子プロセスからフォークアウトするには、nativeForkSystemServer を呼び出します。nativeForkSystemServer はローカル メソッドであり、nativeForkSystemServer メソッドは、startReg メソッドの register_com_android_internal_os_Zygote を介して com_android_inter にマップされています。 nal_os_Zygote_nativeForkSystemServer メソッドについては、興味のある学生が詳細を学ぶことができるものがあります。子プロセスからフォークアウトした後、子プロセスは handleSystemServerProcess() メソッドの呼び出しを開始しますFrameworks/base/core/java/com/android/internal/os/ZygoteInit.java









Private Static Runnable handlesystemServerProcess (zygoteConnection.arguments PARSEDARGS) {     ......     // 環境変数 SystemServerClassPath からパス Fraf File Frade F を取得します Inal     String SystemServerClassPath = OS.Getenv ("SystemServerClassPath");     if (SystemServerClassPath! = null) {         //対応する jar パッケージの dex 最適化を実行します         performSystemServerDexOpt(systemServerClasspath);         …     }







    ......
    ClassLoader cl = null;
    if (systemServerClasspath != null) {         //クラスローダーを作成します classloader         cl = createPathClassLoader(systemServerClasspath, parsedArgs.targetSdkVersion);

        Thread.currentThread().setContextClassLoader(cl);
    }

    return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
}

まず、SYSTEMSERVERCLASSPATH 環境から SystemServer のクラスパスを取得し、次に、performSystemServerDexOpt を使用して、クラスパスに対応する jar パッケージに対して dex 最適化を実行します。次に、後で SystemServer クラスをロードするために使用される対応するクラスローダーを作成すると、ZygoteInit.zygoteInit() が引き続き実行されます:
Frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

public static Final Runnable zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) {     ......     //標準出力ストリームと標準エラーストリームを Android ログにリダイレクトします     RuntimeInit.redirectLogStreams();


    ......
    //ネイティブ メソッド、startReg マッピング、主にバインダー通信用に ProcessState スレッド プールを開く
    ZygoteInit.nativeZygoteInit();
    
    return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
}

redirectLogStreams は標準入力、エラーを返しますストリームは Android ログに再配置され、nativeZygoteInit JNI 関数 (startReg マッピング) がバインダー通信用に ProcessState スレッド プールを開きます。
RuntimeInit.applicationInit() の実行を続けます:
Frameworks/base/core/java/com/android/internal/os/RuntimeInit.java

protected static Runnable applicationInit(int targetSdkVersion, String[] argv,ClassLoader classLoader) {     ......     // 残りの引数は開始クラスの静的メイン     return findStaticMain(args.startClass, args.startArgs, classLoader); }继续往下行:





翻訳なしの英語解説も読み応えがあります。

/**
 * クラス "className" の静的 "main(argv[]) メソッドを呼び出します。 * 失敗したさまざまな例外を  、 * VM インスタンスが終了することを前提として、
 RuntimeExceptions に変換します。  *  * @param className Fully -修飾されたクラス名  * @param argv main() の引数ベクトル  * @param classLoader {@className} をロードするクラスローダー  */ protected static Runnable findStaticMain(String className, String[] argv,ClassLoader classLoader) {     Class<?> cl;     ......     //SystemServer のクラス型     cl = Class.forName(className, true, classLoader);     ......     Method m;     try {














        //メインメソッドのメソッドIDを取得
        m = cl.getMethod("main", new Class[] { String[].class });
    } catch (NoSuchMethodException ex) {         ......     } catch (SecurityException ex ) {         ......     }     //これが③のforkSystemServerの戻り値です r     return new MethodAndArgsCaller(m, argv); } findStaicMainはSystemServerのクラスタイプとSystemServerのメインメソッドのメソッドIDを取得します。すると、new MethodAndArgsCaller(m, argv) が③の forkSystemServer の戻り値 r となり、r.run が何をするのか見てみましょう。フレームワーク/base/core/java/com/android/internal/os/RuntimeInit.java









static class MethodAndArgsCallerimplements Runnable {     /** 呼び出すメソッド */     private Final Method mMethod;     /** 引数配列 */     private Final String[] mArgs;



    public MethodAndArgsCaller(メソッドメソッド, String[] args) {         mMethod = メソッド;         mArgs = 引数;     }


    public void run() {         try {             //mMethod を呼び出します static メソッド             mMethod.invoke(null, new Object[] { mArgs });         } catch (IllegalAccessException ex) {            ......         } catch (InvocationTargetException ex) {             . .....         }     } }上記から、SystemServer クラスの main メソッドがリフレクションを通じて実行されることがわかります。ご存知のとおり、system_server プロセスは、AMS、PMS、PKMS などのコア システム サービスを登録して実行します。これはこの記事の焦点では​​ないため、興味のある学生は引き続き詳しく学習してください~ ④: zygoteServer.runSelectLoop( ) このポーリングは zygote プロセスに無限ループがあり、フォークからの子プロセス (Android アプリケーション プロセス) が終了し、frameworks/base/core/java/com/android/internal/os を実行し続けます。/ZygoteServer.java













//zygote プロセスのポーリングと監視を有効にします。新しいソケット接続を受信します (新しい ZygoteConnection が作成されます)
// そして、これらのリンクからコマンドを読み取り、
Runnable を実行します runSelectLoop(String abiList) {     ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();     ArrayList<ZygoteConnection >peers = new ArrayList<ZygoteConnection>();

    fds.add(mServerSocket.getFileDescriptor());
    ピア.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 = (短い) POLLIN;         }         try {             //开启轮询             Os.poll(pollFds, -1);         catch (ErrnoException ex) {             throw new RuntimeException("ポーリングに失敗しました", ex);         }         for (int i = pollFds.length - 1; i >= 0; --i) {             if ((pollFds[i].revents & POLLIN) == 0) {                 続行;             }
















            
            if (i == 0) {//新しいソケット接続リクエストの場合(新しい接続を確立する)
            //新しい ZygoteConnection リンク
            ZygoteConnection newPeer = acceptCommandPeer(abiList);
            //リンク配列に追加
            peers.add(newPeer) ;
            / / ファイル記述子配列に追加します
            fds.add(newPeer.getFileDesciptor());
            } else {//以前に確立されたソケット リンク (既存の接続上) の場合は、
                試してください {                     // 対応する ZygoteConnection を取得します                     ZygoteConnection connection = Peers.get(i);                     //ZygoteConnection によって送信されたコマンドを実行します                     。 Final Runnable command = connection.processOneCommand(this);



                    if (mIsForkChild) {//ここに子プロセスが入ります
                        ...
                        //終了、コマンドは④の呼び出し元です
                        return コマンド; 
                    } else {//ここに親プロセスが入ります、上記は while 無限ループです、ザイゴートプロセスは決して終了しません
                        ...
                        if (connection.isClosedByPeer()) {                             connection.closeSocket();                             peers.remove(i);                             fds.remove(i);                         }                     }                 } catch (Exception e) {                     .... ..                 } 最後に {








                    mIsForkChild = false;
                }
            }
        }
    }要約すると、上記のプロセスは /dev/socket/zygote のソケットをポーリングし、新しいリンクがある場合は新しい ZygoteConnection を作成し、対応するソケット fd を fds に追加します (
round

Android P が runOnce される前の processOneCommand のメソッド名

フレームワーク/base/core/java/com/android/internal/os/ZygoteConnection.java

実行可能な processOneCommand(ZygoteServer zygoteServer) {     String args[];     引数 parsedArgs = null;     FileDescriptor[] 記述子。     try {     //读取コマンド         args = readArgumentList();         記述子 = mSocket.getAncillaryFileDescriptors();     catch (IOException ex) {         ......     }     ......     parsedArgs = new Arguments(args);     ......     //fork子进程     pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,                 parsedArgs.runtimeFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,



    







    



    



                parsedArgs.niceName、fdsToClose、fdsToIgnore、parsedArgs.startChildZygote、
                parsedArgs.instructionSet、parsedArgs.appDataDir);

    try {         if (pid == 0) {             // 子プロセス内 (アプリケーション プロセス内)             zygoteServer.setForkChild();             ......             return handleChildProc(parsedArgs, descriptors, childPipeFd,                     parsedArgs.startChildZygote);         } else {             / /親プロセス (zygote) 内             ...             return null;         }     }finally {         ...     } }現在接続されているソケットから開始コマンドを読み取ります。読み取りが成功すると、zygote は子プロセスをフォークアウトし、スタートアップ クラスのメイン メソッド (つまり、④の呼び出し元) を呼び出すことができる実行可能ファイルを返します。
















AMS がアプリケーション プロセスの開始を要求した場合、起動クラスは ActivityThread.java であり、caller.run() は反射的に ActivityThread のメイン メソッドを呼び出します。

zygoteServer.setForkChild() は、mIsForkChild グローバル変数を true に設定します。
次に、handleChildProc() メソッド
Frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java を分析します。

private Runnable handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors,FileDescriptor PipeFd, boolean isZygote) { //     ZygoteConnection でソケット リンクを閉じる     closeSocket();     ......     if (parsedArgs.niceName != null) {         Process.setArgV0 (parsedArgs.niceName);     }     ......     if (!isZygote) {         return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs,null /* classLoader */);     } else {           ......     } ZygoteInit.zygoteInit()からは、③で解析したコードと全く同じなので、ここでの解析は省略しますが、③で返されるのは、SystemServerのmainメソッドを呼び出すことができるRunnableであり④で返されるのはActivityThreadメソッドのRunnableを呼び出せるmainです。それ以来、ZygoteInit も分析されています。















まとめ
Zygote プロセスが最初の Java プロセスであると上で述べましたが、記事全体を分析した結果、Java プロセスは実際には C++ プロセスの上で実行されますが、Java 仮想マシンはこれらすべてを保護します。zygote プロセスの開始は、C++ の世界から Java の世界への段階的な移行であり、それぞれの世界が独自の準備を行っています。

C++ の世界 (app_main.cpp エントリ):

仮想マシンの動的ライブラリを動的にロードし、Java 仮想マシンを起動します。
JNI ローカル関数を登録して、仮想マシンの負担を軽減します。
ZygoteInit を Java 仮想マシンにロードし、正式に Java ワールド
Java ワールドに入ります (ZygoteInit.java エントリ)。

新しい Android アプリケーションの実行リクエストを受信するためにソケットをバインド
する Android リソースをプリロードして、アプリケーション プロセスの起動速度を向上させる
SystemServer (AMS、PMS、およびその他のコア サービスを実行する) を起動して実行し、
新しい Android アプリケーションの実行リクエストを処理する
zygote プロセスの起動は実際には行われません 特に難しいのは次のとおりです。ソースコード解析の作業は退屈で、落ち着いて初めて何かを得ることができます。
この記事は、大まかな流れを紹介することを主目的として、多くの詳細を無視しています。間違いがあれば、批判と指摘をしてください〜良い文章だと思われる場合は、高評価をお願いします〜

おすすめ

転載: blog.csdn.net/s_nshine/article/details/131777256