Android 世界中,誰喊醒了 Zygote ?

  • 2019 年 10 月 15 日
  • 筆記

本文基於 Android 9.0 , 程式碼倉庫地址 : android_9.0.0_r45

文中源碼鏈接:

SystemServer.java

ActivityManagerService.java

Process.java

ZygoteProcess.java

ZygoteSystemServer 啟動流程還不熟悉的建議閱讀下面兩篇文章:

Java 世界的盤古和女媧 —— Zygote

Zygote 家的大兒子 —— SystemServer

Zygote 作為 Android 世界的受精卵,在成功繁殖出 system_server 進程之後並沒有完全功成身退,仍然承擔著受精卵的責任。Zygote 通過調用其持有的 ZygoteServer 對象的 runSelectLoop() 方法開始等待客戶端的呼喚,有求必應。客戶端的請求無非是創建應用進程,以 startActivity() 為例,假如開啟的是一個尚未創建進程的應用,那麼就會向 Zygote 請求創建進程。下面將從 客戶端發送請求服務端處理請求 兩方面來進行解析。

客戶端發送請求

startActivity() 的具體流程這裡就不分析了,系列後續文章會寫到。我們直接看到創建進程的 startProcess() 方法,該方法在 ActivityManagerService 中,後面簡稱 AMS

Process.startProcess()

> ActivityManagerService.java    private ProcessStartResult startProcess(String hostingType, String entryPoint,          ProcessRecord app, int uid, int[] gids, int runtimeFlags, int mountExternal,          String seInfo, String requiredAbi, String instructionSet, String invokeWith,          long startTime) {      try {          checkTime(startTime, "startProcess: asking zygote to start proc");          final ProcessStartResult startResult;          if (hostingType.equals("webview_service")) {              startResult = startWebView(entryPoint,                      app.processName, uid, uid, gids, runtimeFlags, mountExternal,                      app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,                      app.info.dataDir, null,                      new String[] {PROC_START_SEQ_IDENT + app.startSeq});          } else {              // 新建進程              startResult = Process.start(entryPoint,                      app.processName, uid, uid, gids, runtimeFlags, mountExternal,                      app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,                      app.info.dataDir, invokeWith,                      new String[] {PROC_START_SEQ_IDENT + app.startSeq});          }          checkTime(startTime, "startProcess: returned from zygote!");          return startResult;      } finally {          Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);      }  }

調用 Process.start() 方法新建進程,繼續追進去:

> Process.java    public static final ProcessStartResult start(                  // android.app.ActivityThread,創建進程後會調用其 main() 方法                  final String processClass,                  final String niceName, // 進程名                  int uid, int gid, int[] gids,                  int runtimeFlags, int mountExternal,                  int targetSdkVersion,                  String seInfo,                  String abi,                  String instructionSet,                  String appDataDir,                  String invokeWith, // 一般新建應用進程時,此參數不為 null                  String[] zygoteArgs) {          return zygoteProcess.start(processClass, niceName, uid, gid, gids,                      runtimeFlags, mountExternal, targetSdkVersion, seInfo,                      abi, instructionSet, appDataDir, invokeWith, zygoteArgs);      }

繼續調用 zygoteProcess.start()

> ZygoteProess.java    public final Process.ProcessStartResult start(final String processClass,                                                final String niceName,                                                int uid, int gid, int[] gids,                                                int runtimeFlags, int mountExternal,                                                int targetSdkVersion,                                                String seInfo,                                                String abi,                                                String instructionSet,                                                String appDataDir,                                                String invokeWith,                                                String[] zygoteArgs) {      try {          return startViaZygote(processClass, niceName, uid, gid, gids,                  runtimeFlags, mountExternal, targetSdkVersion, seInfo,                  abi, instructionSet, appDataDir, invokeWith, false /* startChildZygote */,                  zygoteArgs);      } catch (ZygoteStartFailedEx ex) {          Log.e(LOG_TAG,                  "Starting VM process through Zygote failed");          throw new RuntimeException(                  "Starting VM process through Zygote failed", ex);      }  }

調用 startViaZygote() 方法。終於看到 Zygote 的身影了。

startViaZygote()

> ZygoteProcess.java    private Process.ProcessStartResult startViaZygote(final String processClass,                                                    final String niceName,                                                    final int uid, final int gid,                                                    final int[] gids,                                                    int runtimeFlags, int mountExternal,                                                    int targetSdkVersion,                                                    String seInfo,                                                    String abi,                                                    String instructionSet,                                                    String appDataDir,                                                    String invokeWith,                                                    boolean startChildZygote, // 是否克隆 zygote 進程的所有狀態                                                    String[] extraArgs)                                                    throws ZygoteStartFailedEx {      ArrayList<String> argsForZygote = new ArrayList<String>();        // --runtime-args, --setuid=, --setgid=,      // and --setgroups= must go first      // 處理參數      argsForZygote.add("--runtime-args");      argsForZygote.add("--setuid=" + uid);      argsForZygote.add("--setgid=" + gid);      argsForZygote.add("--runtime-flags=" + runtimeFlags);      if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) {          argsForZygote.add("--mount-external-default");      } else if (mountExternal == Zygote.MOUNT_EXTERNAL_READ) {          argsForZygote.add("--mount-external-read");      } else if (mountExternal == Zygote.MOUNT_EXTERNAL_WRITE) {          argsForZygote.add("--mount-external-write");      }      argsForZygote.add("--target-sdk-version=" + targetSdkVersion);        // --setgroups is a comma-separated list      if (gids != null && gids.length > 0) {          StringBuilder sb = new StringBuilder();          sb.append("--setgroups=");            int sz = gids.length;          for (int i = 0; i < sz; i++) {              if (i != 0) {                  sb.append(',');              }              sb.append(gids[i]);          }            argsForZygote.add(sb.toString());      }        if (niceName != null) {          argsForZygote.add("--nice-name=" + niceName);      }        if (seInfo != null) {          argsForZygote.add("--seinfo=" + seInfo);      }        if (instructionSet != null) {          argsForZygote.add("--instruction-set=" + instructionSet);      }        if (appDataDir != null) {          argsForZygote.add("--app-data-dir=" + appDataDir);      }        if (invokeWith != null) {          argsForZygote.add("--invoke-with");          argsForZygote.add(invokeWith);      }        if (startChildZygote) {          argsForZygote.add("--start-child-zygote");      }        argsForZygote.add(processClass);        if (extraArgs != null) {          for (String arg : extraArgs) {              argsForZygote.add(arg);          }      }        synchronized(mLock) {          // 和 Zygote 進程進行 socket 通訊          return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);      }  }

前面一大串程式碼都是在處理參數,大致瀏覽即可。核心在於最後的 openZygoteSocketIfNeeded()zygoteSendArgsAndGetResult() 這兩個方法。從方法命名就可以看出來,這裡要和 Zygote 進行 socket 通訊了。還記得 ZygoteInit.main() 方法中調用的 registerServerSocketFromEnv() 方法嗎?它在 Zygote 進程中創建了服務端 socket。

openZygoteSocketIfNeeded()

先來看看 openZygoteSocketIfNeeded() 方法。

> ZygoteProcess.java    private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {      Preconditions.checkState(Thread.holdsLock(mLock), "ZygoteProcess lock not held");        // 未連接或者連接已關閉      if (primaryZygoteState == null || primaryZygoteState.isClosed()) {          try {              // 開啟 socket 連接              primaryZygoteState = ZygoteState.connect(mSocket);          } catch (IOException ioe) {              throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);          }          maybeSetApiBlacklistExemptions(primaryZygoteState, false);          maybeSetHiddenApiAccessLogSampleRate(primaryZygoteState);      }      if (primaryZygoteState.matches(abi)) {          return primaryZygoteState;      }        // 當主 zygote 沒有匹配成功,嘗試 connect 第二個 zygote      if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {          try {              secondaryZygoteState = ZygoteState.connect(mSecondarySocket);          } catch (IOException ioe) {              throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe);          }          maybeSetApiBlacklistExemptions(secondaryZygoteState, false);          maybeSetHiddenApiAccessLogSampleRate(secondaryZygoteState);      }        if (secondaryZygoteState.matches(abi)) {          return secondaryZygoteState;      }        throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi);  }

如果與 Zygote 進程的 socket 連接未開啟,則嘗試開啟,可能會產生阻塞和重試。連接調用的是 ZygoteState.connect() 方法,ZygoteStateZygoteProcess 的內部類。

> ZygoteProcess.java    public static class ZygoteState {         final LocalSocket socket;         final DataInputStream inputStream;         final BufferedWriter writer;         final List<String> abiList;           boolean mClosed;           private ZygoteState(LocalSocket socket, DataInputStream inputStream,                 BufferedWriter writer, List<String> abiList) {             this.socket = socket;             this.inputStream = inputStream;             this.writer = writer;             this.abiList = abiList;         }           public static ZygoteState connect(LocalSocketAddress address) throws IOException {             DataInputStream zygoteInputStream = null;             BufferedWriter zygoteWriter = null;             final LocalSocket zygoteSocket = new LocalSocket();               try {                 zygoteSocket.connect(address);                   zygoteInputStream = new DataInputStream(zygoteSocket.getInputStream());                   zygoteWriter = new BufferedWriter(new OutputStreamWriter(                         zygoteSocket.getOutputStream()), 256);             } catch (IOException ex) {                 try {                     zygoteSocket.close();                 } catch (IOException ignore) {                 }                   throw ex;             }               String abiListString = getAbiList(zygoteWriter, zygoteInputStream);             Log.i("Zygote", "Process: zygote socket " + address.getNamespace() + "/"                     + address.getName() + " opened, supported ABIS: " + abiListString);               return new ZygoteState(zygoteSocket, zygoteInputStream, zygoteWriter,                     Arrays.asList(abiListString.split(",")));         }     ...  }

通過 socket 連接 Zygote 遠程服務端。

再回頭看之前的 zygoteSendArgsAndGetResult() 方法。

zygoteSendArgsAndGetResult()

 > ZygoteProcess.java    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();           // 向 zygote 進程發送參數         for (int i = 0; i < sz; i++) {             String arg = args.get(i);             writer.write(arg);             writer.newLine();         }           writer.flush();           // 是不是應該有一個超時時間?         Process.ProcessStartResult result = new Process.ProcessStartResult();           // Always read the entire result from the input stream to avoid leaving         // bytes in the stream for future process starts to accidentally stumble         // upon.         // 讀取 zygote 進程返回的子進程 pid         result.pid = inputStream.readInt();         result.usingWrapper = inputStream.readBoolean();           if (result.pid < 0) { // pid 小於 0 ,fork 失敗             throw new ZygoteStartFailedEx("fork() failed");         }         return result;     } catch (IOException ex) {         zygoteState.close();         throw new ZygoteStartFailedEx(ex);     }  }

通過 socket 發送請求參數,然後等待 Zygote 進程返回子進程 pid 。客戶端的工作到這裡就暫時完成了,我們再追蹤到服務端,看看服務端是如何處理客戶端請求的。

Zygote 處理客戶端請求

Zygote 處理客戶端請求的程式碼在 ZygoteServer.runSelectLoop() 方法中。

> ZygoteServer.java    Runnable runSelectLoop(String abiList) {     ...       while (true) {        ...         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 { // 處理客戶端請求                 try {                     ZygoteConnection connection = peers.get(i);                     // fork 子進程,並返回包含子進程 main() 函數的 Runnable 對象                     final Runnable command = connection.processOneCommand(this);                       if (mIsForkChild) {                         // 位於子進程                         if (command == null) {                             throw new IllegalStateException("command == null");                         }                           return command;                     } else {                         // 位於父進程                         if (command != null) {                             throw new IllegalStateException("command != null");                         }                           if (connection.isClosedByPeer()) {                             connection.closeSocket();                             peers.remove(i);                             fds.remove(i);                         }                     }                 } catch (Exception e) {                     ...                 } finally {                     mIsForkChild = false;                 }             }         }     }  }

acceptCommandPeer() 方法用來響應新客戶端的 socket 連接請求。processOneCommand() 方法用來處理客戶端的一般請求。

processOneCommand()

> ZygoteConnection.java    Runnable processOneCommand(ZygoteServer zygoteServer) {      String args[];      Arguments parsedArgs = null;      FileDescriptor[] descriptors;        try {          // 1. 讀取 socket 客戶端發送過來的參數列表          args = readArgumentList();          descriptors = mSocket.getAncillaryFileDescriptors();      } catch (IOException ex) {          throw new IllegalStateException("IOException on command socket", ex);      }        ...        // 2. 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();              // 關閉服務端 socket              zygoteServer.closeServerSocket();              IoUtils.closeQuietly(serverPipeFd);              serverPipeFd = null;              // 3. 處理子進程事務              return handleChildProc(parsedArgs, descriptors, childPipeFd,                      parsedArgs.startChildZygote);          } else {              // 處於 Zygote 進程              IoUtils.closeQuietly(childPipeFd);              childPipeFd = null;              // 4. 處理父進程事務              handleParentProc(pid, descriptors, serverPipeFd);              return null;          }      } finally {          IoUtils.closeQuietly(childPipeFd);          IoUtils.closeQuietly(serverPipeFd);      }  }

processOneCommand() 方法大致可以分為五步,下面逐步分析。

readArgumentList()

> ZygoteConnection.java    private String[] readArgumentList()          throws IOException {        int argc;        try {          // 逐行讀取參數          String s = mSocketReader.readLine();            if (s == null) {              // EOF reached.              return null;          }          argc = Integer.parseInt(s);      } catch (NumberFormatException ex) {          throw new IOException("invalid wire format");      }        // See bug 1092107: large argc can be used for a DOS attack      if (argc > MAX_ZYGOTE_ARGC) {          throw new IOException("max arg count exceeded");      }        String[] result = new String[argc];      for (int i = 0; i < argc; i++) {          result[i] = mSocketReader.readLine();          if (result[i] == null) {              // We got an unexpected EOF.              throw new IOException("truncated request");          }      }        return result;  }

讀取客戶端發送過來的請求參數。

forkAndSpecialize()

> Zygote.java    public static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,        int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,        int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir) {      VM_HOOKS.preFork();      // Resets nice priority for zygote process.      resetNicePriority();      int pid = nativeForkAndSpecialize(                uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,                fdsToIgnore, startChildZygote, instructionSet, appDataDir);      // Enable tracing as soon as possible for the child process.      if (pid == 0) {          Trace.setTracingEnabled(true, runtimeFlags);            // Note that this event ends at the end of handleChildProc,          Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");      }      VM_HOOKS.postForkCommon();      return pid;  }

nativeForkAndSpecialize() 是一個 native 方法,在底層 fork 了一個新進程,並返回其 pid。不要忘記了這裡的 一次fork,兩次返回pid > 0 說明還是父進程。pid = 0 說明進入了子進程。子進程中會調用 handleChildProc,而父進程中會調用 handleParentProc()

handleChildProc()

> ZygoteConnection.java    private Runnable handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors,          FileDescriptor pipeFd, boolean isZygote) {      closeSocket(); // 關閉 socket 連接      ...        if (parsedArgs.niceName != null) {          // 設置進程名          Process.setArgV0(parsedArgs.niceName);      }        if (parsedArgs.invokeWith != null) {          WrapperInit.execApplication(parsedArgs.invokeWith,                  parsedArgs.niceName, parsedArgs.targetSdkVersion,                  VMRuntime.getCurrentInstructionSet(),                  pipeFd, parsedArgs.remainingArgs);            // Should not get here.          throw new IllegalStateException("WrapperInit.execApplication unexpectedly returned");      } else {          if (!isZygote) { // 新建應用進程時 isZygote 參數為 false              return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs,                      null /* classLoader */);          } else {              return ZygoteInit.childZygoteInit(parsedArgs.targetSdkVersion,                      parsedArgs.remainingArgs, null /* classLoader */);          }      }  }

當看到 ZygoteInit.zygoteInit() 時你應該感覺很熟悉了,接下來的流程就是:

ZygoteInit.zygoteInit() -> RuntimeInit.applicationInit() -> findStaticMain()

SystemServer 進程的創建流程一致。這裡要找的 main 方法就是 ActivityThrad.main()ActivityThread 雖然並不是一個執行緒,但你可以把它理解為應用的主執行緒。

handleParentProc()

> ZygoteConnection.java    private void handleParentProc(int pid, FileDescriptor[] descriptors, FileDescriptor pipeFd) {          if (pid > 0) {              setChildPgid(pid);          }            if (descriptors != null) {              for (FileDescriptor fd: descriptors) {                  IoUtils.closeQuietly(fd);              }          }            boolean usingWrapper = false;          if (pipeFd != null && pid > 0) {              int innerPid = -1;              try {                  // Do a busy loop here. We can't guarantee that a failure (and thus an exception                  // bail) happens in a timely manner.                  final int BYTES_REQUIRED = 4;  // Bytes in an int.                    StructPollfd fds[] = new StructPollfd[] {                          new StructPollfd()                  };                    byte data[] = new byte[BYTES_REQUIRED];                    int remainingSleepTime = WRAPPED_PID_TIMEOUT_MILLIS;                  int dataIndex = 0;                  long startTime = System.nanoTime();                    while (dataIndex < data.length && remainingSleepTime > 0) {                      fds[0].fd = pipeFd;                      fds[0].events = (short) POLLIN;                      fds[0].revents = 0;                      fds[0].userData = null;                        int res = android.system.Os.poll(fds, remainingSleepTime);                      long endTime = System.nanoTime();                      int elapsedTimeMs = (int)((endTime - startTime) / 1000000l);                      remainingSleepTime = WRAPPED_PID_TIMEOUT_MILLIS - elapsedTimeMs;                        if (res > 0) {                          if ((fds[0].revents & POLLIN) != 0) {                              // Only read one byte, so as not to block.                              int readBytes = android.system.Os.read(pipeFd, data, dataIndex, 1);                              if (readBytes < 0) {                                  throw new RuntimeException("Some error");                              }                              dataIndex += readBytes;                          } else {                              // Error case. revents should contain one of the error bits.                              break;                          }                      } else if (res == 0) {                          Log.w(TAG, "Timed out waiting for child.");                      }                  }                    if (dataIndex == data.length) {                      DataInputStream is = new DataInputStream(new ByteArrayInputStream(data));                      innerPid = is.readInt();                  }                    if (innerPid == -1) {                      Log.w(TAG, "Error reading pid from wrapped process, child may have died");                  }              } catch (Exception ex) {                  Log.w(TAG, "Error reading pid from wrapped process, child may have died", ex);              }                // Ensure that the pid reported by the wrapped process is either the              // child process that we forked, or a descendant of it.              if (innerPid > 0) {                  int parentPid = innerPid;                  while (parentPid > 0 && parentPid != pid) {                      parentPid = Process.getParentPid(parentPid);                  }                  if (parentPid > 0) {                      Log.i(TAG, "Wrapped process has pid " + innerPid);                      pid = innerPid;                      usingWrapper = true;                  } else {                      Log.w(TAG, "Wrapped process reported a pid that is not a child of "                              + "the process that we forked: childPid=" + pid                              + " innerPid=" + innerPid);                  }              }          }            try {              mSocketOutStream.writeInt(pid);              mSocketOutStream.writeBoolean(usingWrapper);          } catch (IOException ex) {              throw new IllegalStateException("Error writing to command socket", ex);          }      }

主要進行一些資源清理的工作。到這裡,子進程就創建完成了。

總結

  1. 調用 Process.start() 創建應用進程
  2. ZygoteProcess 負責和 Zygote 進程建立 socket 連接,並將創建進程需要的參數發送給 Zygote 的 socket 服務端
  3. Zygote 服務端接收到參數之後調用 ZygoteConnection.processOneCommand() 處理參數,並 fork 進程
  4. 最後通過 findStaticMain() 找到 ActivityThread 類的 main() 方法並執行,子進程就啟動了

預告

到現在為止已經解析了 Zygote 進程 ,SystemServer 進程,以及應用進程的創建。下一篇的內容是和應用最密切相關的系統服務 ActivityManagerService , 來看看它在 SystemServer 中是如何被創建和啟動的,敬請期待!

文章首發微信公眾號: 秉心說 , 專註 Java 、 Android 原創知識分享,LeetCode 題解。

更多最新原創文章,掃碼關注我吧!