android進階——深入理解應用程序的進程是如何啓動的

一、簡介

      當我們打開android手機的時候,不知道你是否想過app是如何啓動的呢?

        接下來,我將從源碼角度進行解析,當然,本文作爲上篇,是介紹應用程序的進程啓動過程,而不是應用程序的啓動過程,他們的區別就是煮飯前要準備鍋具,沒有鍋具就無法煮飯,本文就是準備鍋具的,但是也不簡單哦。

     文章將從兩個方面介紹,一個AMS發送請求,一個是Zygote接受請求。

     AMS就是Activity Manager System,管理Activity的,而Zygote就是創建進程的一個進程,所以AMS要想創建進程必須從Zygote那裏進行申請,一系列的故事,就此展開。

二、AMS發送請求

從圖中可以簡略看出,首先AMS自己調用了自己的startProcessLocked方法,會創建相應的用戶id,用戶id組,以及對應的應用程序進程的主線程名——android.app.ActivityThread

private final void startProcessLocked(ProcessRecord app, String hostingType,
        String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
    ...
    try {
        int uid = app.uid;
        int[] gids = null;
        int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;
        if (!app.isolated) {
            int[] permGids = null;
            if (ArrayUtils.isEmpty(permGids)) {
                gids = new int[3];
            } else {
                gids = new int[permGids.length + 3];
                System.arraycopy(permGids, 0, gids, 3, permGids.length);
            }
            gids[0] = UserHandle.getSharedAppGid(UserHandle.getAppId(uid));
            gids[1] = UserHandle.getCacheAppGid(UserHandle.getAppId(uid));
            gids[2] = UserHandle.getUserGid(UserHandle.getUserId(uid));
        }
        String requiredAbi = (abiOverride != null) ? abiOverride : app.info.primaryCpuAbi;
        if (requiredAbi == null) {
            requiredAbi = Build.SUPPORTED_ABIS[0];
        }


    ...
        if (entryPoint == null) entryPoint = "android.app.ActivitThread";
        ...
        if (hostingType.equals("webview_service")) {
            ...
        } else {
            startResult = Process.start(entryPoint,
                    app.processName, uid, uid, gids, debugFlags, mountExternal,
                    app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                    app.info.dataDir, invokeWith, entryPointArgs);
        }
       ...
}


    這裏面的start中有一個參數名爲requireAbi,在下面會改成abi起到比對的重要作用。

startProcessLocked方法內部會調用Process的start方法,而這個start方法只幹了一件事,就是調用ZygoteProcessstart方法。

ZygoteProcess的作用在於保持與Zygote通信的狀態,在其start方法中,會調用StartViaZygote方法,這裏的出現了下面很重要的abi。

public final Process.ProcessStartResult start(final String processClass,
                                                  final String niceName,
                                                  int uid, int gid, int[] gids,
                                                  ...
                                                  String abi,
                                                  ...
                                                  String[] zygoteArgs) {
        try {
            return startViaZygote(processClass, niceName, uid, gid, gids,
                    debugFlags, mountExternal, targetSdkVersion, seInfo,
                    abi, instructionSet, appDataDir, invokeWith, zygoteArgs);
       ...
    }


   StartViaZygote方法會維護一個名爲argsForZygote的列表,顧名思義,所有能夠啓動應用程序的進程的參數,都會保存在這裏。然後這個列表作爲一個參數,被傳入到ZygoteSendArgsAndGetResult方法中,這個方法也是在StartViaZygote方法中被調用.另外要注意的是,openZygoteSocketIfNeeded方法也是作爲參數傳入了ZygoteSendArgsAndGetResult方法中。

private Process.ProcessStartResult startViaZygote(final String processClass,
                                                  final String niceName,
                                                  final int uid, final int gid,
                                                  final int[] gids,
                                                  ...
                                                  String abi,
                                                  ...)
                                                  throws ZygoteStartFailedEx {
    ArrayList<String> argsForZygote = new ArrayList<String>();
    argsForZygote.add("--runtime-args");
    argsForZygote.add("--setuid=" + uid);
    argsForZygote.add("--setgid=" + gid);
   ...
    synchronized(mLock) {
        return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
    }
}


  ZygoteSendArgsAndGetResult方法作用是將傳入的參數,也就是argsForZygote中保存的數據寫入ZygoteState中,從圖中我們雖然將ZygoteStateZygoteProcess做了並列處理,但是ZygoteStateZygoteProcess的一個靜態內部類,上面提到的ZygoteProcess的作用在於保持與Zygote通信的狀態,其功能的完成就是在ZygoteState中。

private static Process.ProcessStartResult zygoteSendArgsAndGetResult(
        ZygoteState zygoteState, ArrayList<String> args)
        throws ZygoteStartFailedEx {
    try {
     
        final BufferedWriter writer = zygoteState.writer;
        final DataInputStream inputStream = zygoteState.inputStream;


        writer.write(Integer.toString(args.size()));
        writer.newLine();


        for (int i = 0; i < sz; i++) {
            String arg = args.get(i);
            writer.write(arg);
            writer.newLine();
        }


        writer.flush();
    ...
        result.pid = inputStream.readInt();
        result.usingWrapper = inputStream.readBoolean();
        ...
}


     從上面的方法看出,ZygoteSendArgsAndGetResult方法的第一個參數變成了ZygoteState,也就是說,openZygoteSocketIfNeeded方法的返回值是ZygoteState

openZygoteSocketIfNeeded方法也處於ZygoteProcess中,就寫在StartViaZygote下面,可以看到,在第6行與Zygote進程建立了連接。

private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedE
    Preconditions.checkState(Thread.holdsLock(mLock), "ZygoteProcess lock not held");


    if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
        try {
            primaryZygoteState = ZygoteState.connect(mSocket);//建立連接
        } catch (IOException ioe) {
            throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);
        }
    }
  //這裏primaryZygoteState是連接主模式後返回的ZygoteState,比對其是否與需要的abi匹配
    if (primaryZygoteState.matches(abi)) {
        return primaryZygoteState;
    }
  //如果主模式不成功,連接輔模式
    if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {
        try {
            secondaryZygoteState = ZygoteState.connect(mSecondarySocket);
        } catch (IOException ioe) {
            throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe);
        }
    }
  //匹配輔模式的abi
    if (secondaryZygoteState.matches(abi)) {
        return secondaryZygoteState;
    }


    throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi);
     

     爲什麼第6行可以連接Zygote呢?因爲Zygote是最先創建的進程,如果沒有Zygote,我們的應用程序的進程也無從談起,而Zygote爲了創建其他進程,他會設置一個Socket監聽,就是通過這個Socket,完成與Zygote的連接。

      而對於主模式和輔助模式,這裏不細講,就是有兩次連接並匹配abi的機會。

      要匹配abi的原因在於,Zygote雖然可以fork自己不斷創建進程,但是畢竟是有限的,而且需要資源,只有啓動某個應用程序所必須的進程纔有被創建的意義,這時候就在一開始的AMS中給設置一個abi,如果與後來的Zygote匹配,證明是需要被創建的。

     當匹配完成時,就會返回一個primaryZygoteState,此時AMS發送請求這一流程就結束了,接下來,就是Zygote如何接受請求並創建應用程序的進程的過程了。

三、Zygote接受請求

依然是時序圖,甚至說,有了時序圖,根本就不需要看後面的文章啦。

      看起來比上一個圖稍微複雜些,不過可以發現還是很有規律的,Zygote先到ZygoteServer再回來,再到ZygoteConnection,再回來……

      所以,後面幾個方法都在ZygoteInit中被調用,而且時間先後就是圖中所示。

     在AMS發送請求完成後,Zygote會連接到一個ZygoteState,而這個ZygoteState在之前的ZygoteSendArgsAndGetResult方法中被寫入了argsForZygote所保存的數據,這些數據就是請求的具體參數。

現在Zygote將會收到這些參數,並在ZygoteInit中進行相應操作。

ZygoteInit首先會執行其main方法。

 public static void main(String argv[]) {
       ...
        try {
           ...
            //設置Socket監聽
            zygoteServer.registerServerSocket(socketName);
            if (!enableLazyPreload) {
                bootTimingsTraceLog.traceBegin("ZygotePreload");
                EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
                    SystemClock.uptimeMillis());
                preload(bootTimingsTraceLog);
                EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
                    SystemClock.uptimeMillis());
                bootTimingsTraceLog.traceEnd(); // ZygotePreload
            } else {
                Zygote.resetNicePriority();
           ...
            if (startSystemServer) {
                startSystemServer(abiList, socketName, zygoteServer);//啓動SystemServer進程
            }


            Log.i(TAG, "Accepting command socket connections");
            zygoteServer.runSelectLoop(abiList);//等待AMS請求


            zygoteServer.closeServerSocket();
        ...
    }


      這裏最後一個註釋,就是一切都準備好了,只等AMS發來請求,現在我們看看RunSelectLoop方法是如何做的,猜測既然是等待,那肯定有一個死循環while,這個函數就在ZygoteServer之中。

void runSelectLoop(String abiList) throws Zygote.MethodAndArgsCaller {
    ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
    ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
    fds.add(mServerSocket.getFileDescriptor());
    peers.add(null);
    while (true) {
        ...
        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(this);
                if (done) {
                    peers.remove(i);
                    fds.remove(i);
                }
            }
        }
    }
}


果然有一個while(true),哈哈,獎勵自己晚上早睡十分鐘。

可以看到這裏維護了一個ZygoteConnection型的列表,每當連接不夠時,就會增加,然後一個一個連接的進行執行,執行玩一個連接,就移除一個。

runOnce方法的作用就是讀取argsForZygote所保存的數據,完成交接,這個方法顯然,在ZygoteConnection中。

boolean runOnce(ZygoteServer zygoteServer) throws Zygote.MethodAndArgsCaller {
    String args[];
    Arguments parsedArgs = null;
    FileDescriptor[] descriptors;
    try {
        args = readArgumentList(); //讀取之前寫入的數據
        descriptors = mSocket.getAncillaryFileDescriptors();
    } catch (IOException ex) {
        Log.w(TAG, "IOException on command socket " + ex.getMessage());
        closeSocket();
        return true;
    }
  ...
    try {
        parsedArgs = new Arguments(args);
    ...
        //創建應用程序進程
        pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
                parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
                parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.instructionSet,
                parsedArgs.appDataDir);
    }
    ...
    try {
        if (pid == 0) {//當前邏輯運行在子程序中
            zygoteServer.closeServerSocket();
            IoUtils.closeQuietly(serverPipeFd);
            serverPipeFd = null;
            handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);


            return true;
        }
        ...
    }
}

       如果在18行處的pid返回值爲0,那麼恭喜,此時的子進程已經創建完成,各種id和參數都傳進去了,此時就會調用handleChildProc方法來處理出現的應用程序的進程(還是要提醒,本文創建的是應用程序的進程,而非應用程序本身)。

handleChildProc方法回調了ZygoteInit中的zygoteInit方法,注意,這裏的z是小寫!

private void handleChildProc(Arguments parsedArgs,
        FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
        throws Zygote.MethodAndArgsCaller {
    ...
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    if (parsedArgs.invokeWith != null) {
        WrapperInit.execApplication(parsedArgs.invokeWith,
                parsedArgs.niceName, parsedArgs.targetSdkVersion,
                VMRuntime.getCurrentInstructionSet(),
                pipeFd, parsedArgs.remainingArgs);
    } else {
        ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion,
                parsedArgs.remainingArgs, null /* classLoader */);
    }
}


現在我們回到ZygoteInit中,看看zygoteInit是怎麼寫的。

public static final void zygoteInit(int targetSdkVersion, String[] argv,
        ClassLoader classLoader) throws Zygote.MethodAndArgsCaller {
    if (RuntimeInit.DEBUG) {
        Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from zygote");
    }


    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ZygoteInit");
    RuntimeInit.redirectLogStreams();


    RuntimeInit.commonInit();
    ZygoteInit.nativeZygoteInit();
    RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
}

注意到這個方法是一個靜態的final方法,在12行代碼處,會執行RuntimeInitapplicationInit方法。

protected static void applicationInit(int targetSdkVersion, String[]
        throws Zygote.MethodAndArgsCaller {
    ...
    final Arguments args;
    try {
        args = new Arguments(argv);
    } catch (IllegalArgumentException ex) {
        Slog.e(TAG, ex.getMessage());
        // let the process exit
        return;
    }
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    invokeStaticMain(args.startClass, args.startArgs, classLoader);
}


    同樣在最後一行,調用了invokeStaticMain方法,這裏傳入了三個參數,第一個參數args.startClass就是本文一開始說的那個主線程的名字android.app.MainThread

終於我們到了最後一站!現在我們進入invokeStaticMain方法。

private static void invokeStaticMain(String className, String[] argv, ClassLoader
        throws Zygote.MethodAndArgsCaller {
    Class<?> cl;


    try {
        cl = Class.forName(className, true, classLoader);//反射獲取MainThread
    } 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 });//獲得main
    } 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);
   ...
    throw new Zygote.MethodAndArgsCaller(m, argv);
}

      這裏的className就是我們剛剛提到的args.startClass,也就是主線程名字,通過第6行的反射獲取到對應的類,命名爲c1,然後再通過15行代碼獲取主方法main,並將main傳入到MethodAndArgsCaller方法中,當做異常拋出。

爲什麼這裏要用拋出異常的方法呢?

      聯想到這是最後一步,拋出異常的方式將會清除設置過程中需要的堆棧幀,僅僅留下一個main方法,使得main方法看起來像一個入口方法,畢竟,前面那麼多的工作,那麼多類的流血犧牲,最後都是爲了成就他。當他出現時,前面的東西,也就沒有必要存在了。

現在異常拋出了,誰來接受呢?當然,是main了,main位於ZygoteInit中。

public static void main(String argv[]) {
    ...
        zygoteServer.closeServerSocket();
    } catch (Zygote.MethodAndArgsCaller caller) {
        caller.run();
    } catch (Throwable ex) {
        Log.e(TAG, "System zygote died with exception", ex);
        zygoteServer.closeServerSocket();
        throw ex;
    }
}


可以看到,當出現了一個MethodAndArgsCaller型的異常的時候,main會捕捉到,並且執行他的run方法,這個run方法在MethodAndArgsCaller中,他是一個靜態類。

public static class MethodAndArgsCaller extends Exception
        implements Runnable {
    ...
    public void run() {
        try {
            mMethod.invoke(null, new Object[] { mArgs });
        } catch (IllegalAccessException ex) {
            throw new RuntimeException(ex);
        } catch (InvocationTargetException ex) {
           ...
        }
    }
}


     調用了invoke方法後,ActivityThread裏面的main方法就會被動態調用,應用程序的進程就進入了這一main方法中。

經過上面的種種操作,我們創建了應用程序的進程並且運行了ActivityThread。

小結

      簡單來說就是要想創建應用程序的進程,需要AMS發送請求,傳入主線程名,這一請求必須符合Zygote的abi,並且將請求參數寫在ZygoteState中。

      然後Zygote讀取參數,fork自身去生成新的進程,如果返回的pid爲0,那麼就完成了應用程序的進程的創建。

然後通過反射將主線程名轉換爲主線程的類,獲得其main方法,通過拋出一個異常清除掉之前的堆棧幀,在main方法裏面接受這個異常,並執行run方法以激活main方法,使得應用程序的進程進入到main方法中得以運行。

寫了一早上,呼~,準備喫飯啦,還有什麼不懂的可以評論區提問哦。


作者:神鷹夢澤
鏈接:https://juejin.im/post/5f0165e1e51d453493111cf5

關注我獲取更多知識或者投稿

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章