Le processus init est le premier processus de l'espace utilisateur et le processus zygote est le premier processus Java. Le processus zygote est un processus enfant du processus init, et le processus init exécute le processus zygote en analysant le fichier rc.
Zygote est un processus très important dans le système Android, et sa fonction principale est d'exécuter des applications Android. L'exécution d'une nouvelle application dans le système Android, comme la fécondation et la division d'un ovule, doit être combinée avec le processus Zygote (qui comporte divers éléments et conditions nécessaires au fonctionnement de l'application, tels que : une machine virtuelle, etc. ) éxécuter.
Processus zygote
Lorsque le processus zygote est en cours d'exécution, il initialise la machine virtuelle Art (ou Dalvik) et la démarre. Les applications Android sont écrites en Java, elles ne peuvent pas s'exécuter directement sur linux sous la forme de processus locaux, mais ne peuvent s'exécuter que sur des machines virtuelles. De plus, chaque programme d'application s'exécute dans sa propre machine virtuelle.Si le programme d'application doit réinitialiser et démarrer la machine virtuelle à chaque fois qu'il s'exécute, ce processus prendra un temps assez long. Par conséquent, dans Android, avant l'exécution du programme d'application, le processus zygote réduit le temps d'exécution du programme d'application en partageant le code et les informations de mémoire de la machine virtuelle en cours d'exécution. De plus, il chargera à l'avance les classes et les ressources du framework Android à utiliser par l'application dans la mémoire, et organisera et formera les informations de lien des ressources utilisées. Lorsqu'une application Android nouvellement en cours d'exécution utilise les ressources requises, il n'est pas nécessaire de régénérer les informations de lien des ressources à chaque fois, ce qui permet de gagner beaucoup de temps et d'améliorer la vitesse d'exécution du programme.
Alors, quelle est la différence entre créer et exécuter un processus dans le système Linux et créer et exécuter une application via zygote dans le système Android ?
Les images suivantes sont toutes référencées dans le livre "Uncovering the Android Framework" (je ne veux vraiment pas dessiner 0.0 moi-même)
Comme le montre la figure ci-dessus, le processus parent A appelle la fonction fork() pour créer un nouveau processus enfant A'. Le processus nouvellement créé A' partage les informations de structure de mémoire et les informations de connexion à la bibliothèque du processus parent (technologie de copie sur écriture COW). Ensuite, le processus enfant A' appelle exec('B') pour charger le code du nouveau processus B en mémoire. À ce stade, la mémoire sera réallouée (parce qu'elle était partagée auparavant) afin d'exécuter le programme B chargé, puis de nouvelles informations de connexion à la bibliothèque seront formées pour que le processus B puisse les utiliser seul.
Comme le montre la figure ci-dessus, le processus zygote appelle la fonction fork() pour créer un processus enfant zygote', et le processus enfant zygote' partage la zone de code et les informations de liaison du processus parent zygote. Notez que la nouvelle application Android A ne recharge pas la zone de code du processus existant via exec(), mais la charge dynamiquement sur la machine virtuelle copiée (la copie de l'espace virtuel, l'espace physique est toujours le même). Ensuite, le processus zygote transmettra le processus d'exécution à la méthode de la classe d'application A, et l'application Android commencera à s'exécuter. L'application A nouvellement générée utilisera les informations de lien de la bibliothèque et les ressources du processus zygote existant, de sorte qu'elle s'exécute très rapidement.
Comme le montre la figure ci-dessus, après le démarrage de zygote, il initialise et exécute la machine virtuelle art (ou dalvik), puis charge les classes et ressources requises en mémoire. Appelez ensuite fork() pour créer un processus enfant zygote, puis le processus enfant zygote charge et exécute dynamiquement l'application Android A. L'application A en cours d'exécution utilisera le code de la machine virtuelle que zygote a initialisé et a commencé à exécuter, et accélérera l'opération en utilisant les classes et les ressources qui ont été enregistrées en mémoire.
COW (Copy on write)
Après avoir créé un nouveau processus, le nouveau processus partagera l'espace mémoire du processus parent, c'est-à-dire que le nouveau processus enfant copiera toutes les informations liées à l'espace mémoire du processus parent et l'utilisera. COW est une technologie de réplication de mémoire. De manière générale, la surcharge de copie de mémoire est très élevée, donc lorsque le processus enfant créé fait référence à l'espace mémoire du processus parent, ne le copiez pas, mais partagez directement l'espace mémoire du processus parent. Lorsque les informations de la mémoire partagée doivent être modifiées, le processus fils va copier les informations mémoire pertinentes du processus parent dans son propre espace mémoire et les modifier, c'est la technologie COW (Copy on write).
Après fork et avant exec, les deux processus utilisent le même espace physique (zone mémoire). Le segment de code, le segment de données et la pile du processus enfant pointent tous vers l'espace physique du processus parent, c'est-à-dire le virtuel. l'espace des deux est différent, mais l'espace physique correspondant est le même.
Analyse du code source du processus Zygote
Exécuté par app_process Classe ZygoteInit
zygote est écrit en Java et ne peut pas être démarré et exécuté directement par le processus init. Si vous souhaitez exécuter la classe zygote, vous devez d'abord créer une machine virtuelle, puis exécuter la classe ZygoteInit sur la machine virtuelle. Ce qui exécute cette tâche est le programme app_process.
Analysons le processus de démarrage du processus zygote :
/system/core/rootdir/init.rc
import /init.$(ro.zygote).rc
S'il s'agit d'un système 64 bits, la valeur de $(ro.zygote) est "zygote64"
/system/core/rootdir/init.zygote64.rc
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
...
classe utilisateur principal
root
socket zygote stream 660 root system //Create /dev/socket/zygote socket
. ..
Une fois que le processus init a analysé le fichier init.zygote64.rc ci-dessus, il exécute le processus /system/bin/app_process64 et génère un socket /dev/socket/zygote, que ZygoteInit utilisera pour recevoir le message AMS. Une requête pour créer un nouvelle application.
Le processus init analyse et exécute le processus du fichier rc, vous pouvez voir mon article précédent Le processus de démarrage du processus Init.
L'entrée de fonction principale du programme app_process64 est la suivante :
frameworks/base/cmds/app_process/app_main.cpp
int main(int argc, char* const argv[])
{ ...... AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv)); ...... // Analyse les arguments d'exécution. Arrêtez-vous à la première option non reconnue. bool zygote = faux ; bool startSystemServer = faux ; ......
tandis que (i < argc) { const char* arg = argv[i++] ; if (strcmp(arg, "--zygote") == 0) { zygote = vrai ; } sinon si (strcmp(arg, "--start-system-server") == 0) { startSystemServer = true; } ...... }
......
if (zygote) { // zygote process runtime.start("com.android.internal.os.ZygoteInit", args, zygote); } else if (className) { // exécution du processus java ordinaire . start("com.android.internal.os.RuntimeInit", args, zygote); } } S'il s'agit d'un processus zygote, exécutez le code ZygoteInit, s'il s'agit d'un processus Java normal, exécutez le code RuntimeInit, tel que le généralement utilisé la commande am, /system/bin/am est en fait un script shell.En regardant le code à l'intérieur, on peut voir que le processus java ordinaire est démarré via app_process, puis communique avec AMS. frameworks/base/core/jni/AndroidRuntime.cpp
//AppRuntime hérite d'AndroidRuntime
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{ /* démarre la machine virtuelle */ JniInvocation jni_invocation; // ① Charge la bibliothèque de la machine virtuelle spécifiée machine (art ou dalvik) jni_invocation.Init(NULL); JNIEnv* env; // ② Créer une machine virtuelle java if (startVm(&mJavaVM, &env, zygote) != 0) { return; }
// ③ Enregistrez la fonction JNI
if (startReg(env) < 0) { return; }
......
// ④ Entrez officiellement dans le monde java, appelez la méthode principale de ZygoteInit.java
jclass startClass = env->FindClass(slashClassName);
......
jmethodID startMeth = env->GetStaticMethodID(startClass, " main","([Ljava/lang/String;)V");
......
env->CallStaticVoidMethod(startClass, startMeth, strArray);
......
}
① : Charger la bibliothèque du machine virtuelle (art ou dalvik), et le pointeur de fonction pointe sur la fonction correspondant à la librairie (ex : fonction JNI_CreateJavaVM)
libnativehelper/JniInvocation.cpp
bool JniInvocation::Init(const char* library) { //Obtenir le nom de la bibliothèque à charger library = GetLibrary(library, buffer);
//Charger dynamiquement la bibliothèque de machine virtuelle correspondante
handle_ = dlopen(library, RTLD_NOW);
......
//JNI_CreateJavaVM_ pointeur de fonction pointe vers la fonction JNI_CreateJavaVM dans la bibliothèque de machine virtuelle
if (!FindSymbol(reinterpret_cast<void**> ( &JNI_CreateJavaVM_),
"JNI_CreateJavaVM")) { return false; } ....... return true; } La fonction GetLibraray() obtient le nom de la bibliothèque dynamique (libart.so ou libdalvik .so), et le dlopen () charge dynamiquement la bibliothèque de la machine virtuelle, RTLD_NOW signifie lier tous les symboles non localisés immédiatement avant de revenir, et le pointeur de fonction FindSysmbol() pointe vers la fonction correspondante. libnativehelper/JniInvocation.cpp
static const char* kLibrarySystemProperty = "persist.sys.dalvik.vm.lib.2" ;
statique const char* kDebuggableSystemProperty = "ro.debuggable" ;
statique const char* kDebuggableFallback = "0" ; // Non débogable.
static const char* kLibraryFallback = "libart.so";
const char* JniInvocation::GetLibrary(const char* bibliothèque, char* tampon) { char débogable[PROPERTY_VALUE_MAX] ; // Property_get(kDebuggableSystemProperty, debuggable,kDebuggableFallback);
if (strcmp(debuggable, "1") != 0) { //S'il ne s'agit pas d'un périphérique de débogage, la valeur de la bibliothèque est "libart.so" library = kLibraryFallback ; } else { //S'il s'agit d'un périphérique de débogage , la valeur de la bibliothèque est " Persisit.sys.dalvik.vm.lib.2" valeur de la propriété property_get(kLibrarySystemProperty, buffer, kLibraryFallback); }
bibliothèque de retour ;
}
: créer des frameworks de machine virtuelle Java
/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 ;
......
// Créer une machine virtuelle Java
if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) { ALOGE("JNI_CreateJavaVM failed\n"); return -1; }
return 0;
}
JNI_CreateJavaVM() est une fonction dans JniInvocation.cpp, elle appellera le pointeur de fonction JNI_CreateJavaVM_() mentionné dans ①, et enfin appellera la fonction JNI_CreateJavaVM() dans la bibliothèque dynamique de la machine virtuelle correspondante pour créer la machine virtuelle correspondante. initArgs représente les paramètres entrants de la machine virtuelle.
③ : Enregistrer les fonctions locales JNI
Familiarisons-nous d'abord avec plusieurs structures de données :
frameworks/base/core/jni/AndroidRuntime.cpp
#define REG_JNI(nom) { nom }
struct RegJNIRec { int (*mProc)(JNIEnv*); } ; ...... statique 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_SystemClock ), REG_JNI(register_android_util_EventLog), REG_JNI (register_android_util_Log), ...... } ; gRegJNI est un tableau de structures RegJNIRec, puis le tableau est initialisé via la définition de macro REG_JNI. Par exemple, l'initialisation du premier élément de gRegJNI équivaut à :
gRegJNI[0] = {register_com_android_internal_os_RuntimeInit} ;
Ensuite, le pointeur de fonction mProc de la structure RegJNIRec dans gRegJNI[0] pointe vers la fonction ci-dessus register_com_android_internal_os_RuntimeInit, et il en va de même pour les autres éléments du tableau. Continuons à analyser l'enregistrement des fonctions locales JNI.
frameworks/base/core/jni/AndroidRuntime.cpp
int AndroidRuntime::startReg(JNIEnv* env)
{ ...... //Enregistrer la fonction locale JNI, passer le tableau gRegJNI dans if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) { return -1; } ...... renvoie 0 ; }
static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env)
{ //Traverse et exécute la fonction mProc des éléments du tableau gRegJNI //(mProc est un pointeur de fonction et le tableau a déjà pointé vers le fonction spécifiée lorsque le tableau est initialisé) for (size_t i = 0; i < count; i++) { if (array[i].mProc(env) < 0) { return -1; } } return 0; } startReg appellera register_jni_procs pour parcourir et appeler la fonction mProc dans le tableau gRegJNI pour prendre le premier élément comme exemple, gRegJNI[0].mProc(env) D'après l'analyse ci-dessus, on peut savoir que la fonction appelée register_com_android_internal_os_RuntimeInit(env) est en fait appelée . frameworks/base/core/jni/AndroidRuntime.cpp
int }; ...... (void*) com_android_internal_os_RuntimeInit_nativeFinishInit }, { "nativeFinishInit", "()V", const JNINativeMethod methodes[] = {
{ register_com_android_internal_os_RuntimeInit(JNIEnv* env) jniRegisterNativeMethods (env, "com/android/internal/os/RuntimeInit", méthodes, NELEM(méthodes)); }
Comme on peut le voir dans le code ci-dessus, la fonction nativeFinishInit mappe la fonction com_android_internal_os_RuntimeInit_nativeFinishInit. Lorsque java appelle la fonction nativeFinishInit, il appelle en fait la fonction com_android_internal_os_RuntimeInit_nativeFinishInit en c/c++. Certains lecteurs peuvent se demander, lorsque java appelle la fonction jni, la machine virtuelle est automatiquement mappée, pourquoi avez-vous besoin de la mapper vous-même ? S'il y a moins de fonctions jni, c'est effectivement faisable, mais nous pouvons voir que le tableau gRegJNI est très grand, et il y a beaucoup de fonctions qui doivent être mappées. Si toutes sont assignées à la machine virtuelle pour le mappage, l'exécution les performances de la machine virtuelle seront considérablement réduites, nous avançons donc Enregistrez la fonction JNI, et la machine virtuelle peut directement trouver la fonction correspondante à appeler.
④ : Appelez la fonction principale de ZygoteInit.java par réflexion et entrez officiellement dans le monde de java. env->FindClass obtient le type de la classe ZygoteInit, env->GetStaticMethodID obtient l'identifiant de la fonction main, env->CallStaticVoidMethod appelle la fonction statique main de ZygoteInit.java.
Fonctions de la classe ZygoteInit
Jusqu'à présent, nous avons créé une machine virtuelle et chargé la classe ZygoteInit sur la machine virtuelle. Ensuite, la classe ZygoteInit sera exécutée, alors quelles sont les fonctions spécifiques de la classe ZygoteInit ? Elle peut se résumer grosso modo comme suit :
Liez le socket pour recevoir la requête en cours d'exécution de la nouvelle application Android.
Préchargez les classes et les ressources utilisées par le framework d'application Android.
Démarrez et exécutez le SystemServer
pour traiter la requête en cours d'exécution de la nouvelle application Android.
La fonction principale de ZygoteInit :
frameworks/ base/core/java/com /android/interne/os/ZygoteInit.java
public static void main(String argv[]) { ZygoteServer zygoteServer = new ZygoteServer(); ...... essayez{ booléen startSystemServer = false ; String socketName = "zygote";//套接字默认名称zygote for (int i = 1; i < argv.length; i++) { if ("start-system-server".equals(argv[i])) { startSystemServer = vrai ; } ...... }
// ① Liez /dev/socket/zygote socket pour recevoir la demande d'exécution de l'application Android
zygoteServer.registerServerSocketFromEnv(socketName);
......
if (!enableLazyPreload) { ...... // ② Précharger les classes et les ressources preload (bootTimingsTraceLog); ...... }
......
if (startSystemServer) { // ③ fork出system_server子进程 Runnable r = forkSystemServer(abiList, socketName, zygoteServer);
// ③ {@code r == null} signifie le processus parent (zygote), {@code r != null} dans le processus enfant (system_server)
if (r != null) { // ③ S'il s'agit d'un enfant process (system_server ) exécute la méthode run() et retourne, et le processus parent (zygote) continue à exécuter r.run(); return; } }
Log.i(TAG, "Accepting command socket connections");
// ④ Cette interrogation bouclera à l'infini dans le processus zygote et le processus enfant forké (processus d'application Android) quittera
l'appelant = zygoteServer.runSelectLoop(abiList);
} catch (Throwable ex) { Log.e(TAG, "System zygote mort avec exception", ex); throw ex; } finally { //le processus system_server et le processus d'application android fermeront le socket, zygote interroge et écoute toujours le socket dans runSelectLoop zygoteServer.closeServerSocket(); }
// ④ Le processus de l'application Android viendra ici et exécutera la commande correspondante
if (caller != null) { caller.run(); } } ① : Liez le socket pour recevoir la requête en cours d'exécution de l'application Android frameworks/base /core /java/com/android/internal/os/ZygoteServer.java
void registerServerSocketFromEnv(String socketName) { if (mServerSocket == null) { int fileDesc; //fullSocketName is "ANDROID_SOCKET_zygote" final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName; try { //Obtenir les variables d'environnement d'ANDROID_SOCKET_zygote (ie /dev/socket /The valeur du descripteur de fichier de zygote) //est la chaîne enregistrée dans la variable d'environnement par le processus init lors du démarrage du processus zygote env = System.getenv(fullSocketName); fileDesc = Integer.parseInt(env); } catch (RuntimeException ex ) { throw new RuntimeException(fullSocketName + "unset or invalid", ex); } try {
// Socket de liaison, utilisé ultérieurement pour recevoir la requête de démarrage de l'application Android
FileDescriptor fd = new FileDescriptor();
fd.setInt$(fileDesc);
mServerSocket = new LocalServerSocket(fd);
mCloseSocketFd = true;
} catch (IOException ex) { .. .... } } } ② : précharger les classes et les ressources, et le processus d'application dérivé du processus zygote peut être partagé directement pour accélérer le démarrage du processus d'application. frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
static void preload(TimingsTraceLog bootTimingsTraceLog) { ...... preloadClasses(); ...... preloadResources(); ...... nativePreloadAppProcessHALs(); ...... preloadOpenGL(); preloadSharedLibraries( ); preloadTextResources(); ...... } preloadClasses, preloadResources, nativePreloadAppProcessHALs préchargent les classes et les ressources, etc. Les étudiants intéressés peuvent en apprendre davantage à ce sujet. ③ : forkSystemServer sort le processus enfant system_server et renvoie le Runnable r qui peut appeler la méthode principale dans SystemServer et exécute la méthode d'exécution correspondante, tandis que le processus parent zygote continue d'exécuter runSelectLoop et écoute les demandes d'exécution d'applications Android. frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
private static Runnable forkSystemServer(String abiList, String socketName,ZygoteServer zygoteServer) { ...... /* Ligne de commande codée en dur pour démarrer le serveur système */ String args[] = { "--setuid=1000", //user id "--setgid=1000", //identifiant de groupe ...... "--nice-name=system_server", ...... "com.android.server.SystemServer", } ; ZygoteConnection.Arguments parsedArgs = null; int pid; try { parsedArgs = new ZygoteConnection.Arguments(args); ...... //fork le processus enfant system_server et définit le paramètre correspondant pid = Zygote.forkSystemServer(
parsedArgs.uid, parsedArgs.gid,
parsedArgs.gids,
parsedArgs.runtimeFlags,
null,
parsedArgs.permittedCapabilities,
parsedArgs.effectiveCapabilities);
} catch (IllegalArgumentException ex) { throw new RuntimeException(ex ); } //processus enfant (system_server) if (pid == 0) { ...... //Donc, le r renvoyé n'est pas nul, exécutez directement r.run return handleSystemServerProcess(parsedArgs); } //Processus parent (zygote), le retour est nul, passez au suivant exécuter retour null ; }
La méthode forkSystemServer de ZygoteInit appellera la méthode forkSystemServer de Zygote. S'il s'agit d'un processus enfant (system_server), il renverra handleSystemServerProcess() et le processus parent (zygote) renverra null.
frameworks/base/core/java/com/android/internal/os/Zygote.java
public static int forkSystemServer(int uid, int gid, int[] gids, int runtimeFlags, int[][] rlimits, long permitCapabilities, long effectiveCapabilities) { ...... int pid = nativeForkSystemServer(uid, gid, gids , runtimeFlags, rlimits, allowCapabilities, effectiveCapabilities); ...... return pid; } Appelez nativeForkSystemServer pour sortir du processus enfant, nativeForkSystemServer est une méthode locale et la méthode nativeForkSystemServer a été mappée à com_android_inter via register_com_android_internal_os_Zygote dans la méthode startReg Sur la méthode nal_os_Zygote_nativeForkSystemServer, les étudiants intéressés peuvent en savoir plus. Après avoir quitté le processus enfant, le processus enfant commence à appeler la méthode handleSystemServerProcess() frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
Private Static Runnable handlesystemServerProcess (zygoteConnection.arguments PARSEDARGS) { ...... // À partir de la variable d'environnement SystemServerClassPath pour obtenir le chemin Fraf File Frade F. Inal String SystemServerClassPath = OS.Getenv ("SystemServerClassPath"); if (SystemServerClassPath ! = null) { //Faire l'optimisation dex pour le package jar correspondant performSystemServerDexOpt(systemServerClasspath); … }
......
ClassLoader cl = null;
if (systemServerClasspath != null) { //Créer un chargeur de classe classloader cl = createPathClassLoader(systemServerClasspath, parsedArgs.targetSdkVersion);
Thread.currentThread().setContextClassLoader(cl);
}
return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
}
Obtenez d'abord le chemin de classe de SystemServer à partir de l'environnement SYSTEMSERVERCLASSPATH, puis utilisez performSystemServerDexOpt pour effectuer l'optimisation dex sur le package jar correspondant au chemin de classe. Créez ensuite le chargeur de classe correspondant, qui est utilisé pour charger la classe SystemServer ultérieurement, et ZygoteInit.zygoteInit() continue de s'exécuter :
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
public static final Runnable zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) { ...... // Redirige le flux de sortie standard et le flux d'erreur standard vers le journal Android RuntimeInit.redirectLogStreams();
......
//Méthode native, mappage startReg, principalement pour ouvrir le pool de threads ProcessState pour la communication du classeur
ZygoteInit.nativeZygoteInit();
return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
}
redirectLogStreams will standard input , the error stream est déplacé vers le journal Android, et la fonction nativeZygoteInit JNI (mappage startReg) ouvrira le pool de threads ProcessState pour la communication du classeur.
Continuez à exécuter RuntimeInit.applicationInit() :
frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
protected static Runnable applicationInit(int targetSdkVersion, String[] argv,ClassLoader classLoader) { ...... // Les arguments restants sont passés au retour principal statique de la classe de démarrage findStaticMain(args.startClass, args.startArgs, classLoader); }继续往下执行:
Le commentaire en anglais sans traduction vaut également la peine d'être lu.
/**
* Invoque une méthode statique "main(argv[]) sur la classe "className".
* Convertit diverses exceptions défaillantes en RuntimeExceptions, avec
* l'hypothèse qu'elles entraîneront ensuite la fermeture de l'instance de VM.
*
* @param className Entièrement -nom de classe qualifié
* @param argv Vecteur d'argument pour main()
* @param classLoader le chargeur de classe à charger {@className} avec
*/
protected static Runnable findStaticMain(String className, String[] argv,ClassLoader classLoader) { Class<?> cl; ...... //获取到SystemServer的类类型 cl = Class.forName(className, true, classLoader); ...... Method m; try {
// Récupère l'ID de méthode de la méthode principale
m = cl.getMethod("main", new Class[] { String[].class });
} catch (NoSuchMethodException ex) { ...... } catch (SecurityException ex ) { ...... } // Ceci est la valeur de retour de forkSystemServer dans ③ r return new MethodAndArgsCaller(m, argv); } findStaicMain obtient le type de classe de SystemServer et l'ID de méthode de la méthode principale dans SystemServer. Alors new MethodAndArgsCaller(m, argv) est la valeur de retour r de forkSystemServer dans ③, voyons ce que fait r.run. frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
la classe statique MethodAndArgsCaller implémente la méthode Runnable { /** pour appeler */ la méthode finale privée mMethod ; /** tableau d'arguments */ private final String[] mArgs ;
public MethodAndArgsCaller(Method method, String[] args) { mMethod = method; mArgs = args ; }
public void run() { try { //Invoke mMethod static method mMethod.invoke(null, new Object[] { mArgs }); } catch (IllegalAccessException ex) { ...... } catch (InvocationTargetException ex) { . ..... } } } Il ressort de ce qui précède que la méthode principale de la classe SystemServer est exécutée par réflexion. Comme nous le savons tous, le processus system_server enregistre et exécute les services système de base tels que AMS, PMS et PKMS, car ce n'est pas l'objet de cet article, les étudiants intéressés peuvent donc continuer à en savoir plus ~ ④ : zygoteServer.runSelectLoop( ) Cette interrogation sera dans Il y a une boucle infinie dans le processus zygote, et le processus enfant (processus d'application Android) hors du fork se fermera et continuera à exécuter frameworks/base/core/java/com/android/internal/os /ZygoteServer.java
// Activer l'interrogation et la surveillance du processus zygote. Recevoir de nouvelles connexions socket (une nouvelle ZygoteConnection sera créée)
//et lire les commandes à partir de ces liens, et exécuter
Runnable runSelectLoop(String abiList) { ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>(); ArrayList<ZygoteConnection > peers = new ArrayList<ConnexionZygote>();
fds.add(mServerSocket.getFileDescriptor());
pairs.add(null);
tandis que (true) { StructPollfd[] pollFds = new StructPollfd[fds.size()] ; for (int je = 0; je < pollFds.length; ++i) { pollFds[i] = new StructPollfd(); pollFds[i].fd = fds.get(i); pollFds[i].events = (court) POLLIN ; } essayez { //开启轮询 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) {//S'il s'agit d'une nouvelle demande de connexion socket (établir une nouvelle connexion)
//Nouveau lien ZygoteConnection
ZygoteConnection newPeer = acceptCommandPeer(abiList);
//Ajouter au tableau de liens
peers.add(newPeer) ;
/ /Ajouter au tableau de descripteurs de fichiers
fds.add(newPeer.getFileDesciptor());
} else {//S'il s'agit d'un lien socket précédemment établi (sur une connexion existante)
try { //Obtenir la connexion ZygoteConnection ZygoteConnection correspondante = peers.get(i); // exécutera la commande envoyée par ZygoteConnection final Runnable command = connection.processOneCommand(this);
if (mIsForkChild) {//Le processus enfant va ici
...
//Quitter, la commande est l'appelant dans la ④
commande de retour ;
} else {//Le processus parent va ici, ce qui précède est une boucle infinie while, le zygote le processus ne se terminera jamais
...
if (connection.isClosedByPeer()) { connection.closeSocket(); peers.remove(i); fds.remove(i); } } } catch (Exception e) { .... .. } enfin {
mIsForkChild = false;
}
} } }
En résumé, le processus ci-dessus interrogera le socket de /dev/socket/zygote, s'il y a un nouveau lien, créera un nouveau ZygoteConnection et ajoutera le socket correspondant fd à fds ( round
Le nom de la méthode de processOneCommand avant qu'Android P ne soit runOnce
frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java
Exécutable processOneCommand(ZygoteServer zygoteServer) { String args[]; Arguments analysésArgs = null ; descripteurs FileDescriptor[] ; try { //读取命令 args = readArgumentList(); descripteurs = 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) { // Dans le processus enfant (dans le processus d'application) zygoteServer.setForkChild(); ...... return handleChildProc(parsedArgs, descriptors, childPipeFd, parsedArgs.startChildZygote); } else { / /dans le processus parent (zygote) ... return null ; } } finally { ... } } Lit la commande de démarrage à partir du socket actuellement connecté. Si la lecture réussit, zygote débranchera le processus enfant et renverra un exécutable qui peut appeler la méthode principale de la classe de démarrage (c'est-à-dire l'appelant en ④)
Si AMS demande de démarrer le processus d'application, la classe de démarrage est ActivityThread.java et caller.run() appellera de manière réflexive la méthode principale de ActivityThread.
zygoteServer.setForkChild() définit la variable globale mIsForkChild sur true.
Nous analysons ensuite la méthode handleChildProc()
frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java
private Runnable handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors,FileDescriptor pipeFd, boolean isZygote) { //Ferme le lien socket dans ZygoteConnection closeSocket(); ...... if (parsedArgs.niceName != null) { Process.setArgV0 (parsedArgs.niceName); } ...... if (!isZygote) { return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs,null /* classLoader */); } else { ...... } } A partir de ZygoteInit.zygoteInit(), c'est exactement le même que le code analysé en ③, et l'analyse ne sera pas répétée ici. Ce qui est retourné en ③ est le Runnable qui peut appeler la méthode principale de SystemServer, et ce qui est retourné dans ④ est le main qui peut appeler Runnable de la méthode ActivityThread. Depuis, ZygoteInit a également été analysé.
Résumé
Nous avons mentionné ci-dessus que le processus Zygote est le premier processus Java, mais après l'analyse de tout l'article, le processus Java s'exécute en fait au-dessus du processus C++, mais la machine virtuelle Java protège tout cela. Le début du processus zygote est une transition étape par étape du monde c++ au monde java, et chaque monde a fait ses propres préparatifs.
monde c++ (entrée app_main.cpp) :
Charger dynamiquement la bibliothèque dynamique de la machine virtuelle et démarrer la machine virtuelle java
Enregistrer les fonctions locales JNI pour réduire la charge sur la machine virtuelle
Charger ZygoteInit sur la machine virtuelle java et entrer officiellement dans le monde java monde java
(entrée ZygoteInit.java) :
Liez les sockets pour recevoir de nouvelles demandes d'exécution d'applications Android
Préchargez les ressources Android pour améliorer la vitesse de démarrage du processus d'application
Démarrez et exécutez SystemServer (exécutant AMS, PMS et d'autres services de base)
pour traiter les nouvelles demandes d'exécution d'applications Android
Le démarrage du processus zygote n'est en fait pas Les plus difficiles sont principalement lourd. Le processus d'analyse du code source est ennuyeux. Ce n'est que lorsque vous vous calmez que vous pouvez gagner quelque chose.
Cet article ignore beaucoup de détails, principalement pour présenter le processus général. S'il y a des erreurs, veuillez critiquer et signaler~ Si vous pensez que l'écriture est bonne, merci de lui donner un coup de pouce~