Android 系统进程和应用进程交互

系统进程初始化

拉起ZygoteInit

Android系统启动的入口是Init进程,它是由init语言编写,通常init.rc中会导入其他路径的配置文件,这样就提供了开机配置的可能性。

init.zygote32.rc负责拉起Zygote,其在AOSP中的路径如下:

/system/core/rootdir/init.zygote32.rc

包含如下基本信息(部分):

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
class main
socket zygote stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
onrestart restart netd
writepid /dev/cpuset/foreground/tasks

第一行:

service zygote /system/bin/app_process -Xzygote /system/bin –zygote –start-system-server

Zygote是以service配置的,所以当解析该完成时,init进程就会调用fork去创建Zygote进程,并且执行app_main.cpp文件中的main函数。

作为Zygote进行的启动入口,app_main.cpp文件的目录路径为:frameworks\base\cmds\app_process\app_main.cpp

部分源码如下:

int main(int argc, char* const argv[])
{
    if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {
        // Older kernels don't understand PR_SET_NO_NEW_PRIVS and return
        // EINVAL. Don't die on such kernels.
        if (errno != EINVAL) {
            LOG_ALWAYS_FATAL("PR_SET_NO_NEW_PRIVS failed: %s", strerror(errno));
            return 12;
        }
    }

    AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
    // Process command line arguments
    // ignore argv[0]
    argc--;
    argv++;

    // Everything up to '--' or first non '-' arg goes to the vm.
    //
    // The first argument after the VM args is the "parent dir", which
    // is currently unused.
    //
    // After the parent dir, we expect one or more the following internal
    // arguments :
    //
    // --zygote : Start in zygote mode
    // --start-system-server : Start the system server.
    // --application : Start in application (stand alone, non zygote) mode.
    // --nice-name : The nice name for this process.
    //
    // For non zygote starts, these arguments will be followed by
    // the main class name. All remaining arguments are passed to
    // the main method of this class.
    //
    // For zygote starts, all remaining arguments are passed to the zygote.
    // main function.
    //
    // Note that we must copy argument string values since we will rewrite the
    // entire argument block when we apply the nice name to argv0.

    int i;
    for (i = 0; i < argc; i++) {
        if (argv[i][0] != '-') {
            break;
        }
        if (argv[i][1] == '-' && argv[i][2] == 0) {
            ++i; // Skip --.
            break;
        }
        runtime.addOption(strdup(argv[i]));
    }

    // Parse runtime arguments.  Stop at first unrecognized option.
    bool zygote = false;
    bool startSystemServer = false;
    bool application = false;
    String8 niceName;
    String8 className;

    ++i;  // Skip unused "parent dir" argument.
    while (i < argc) {
        const char* arg = argv[i++];
        if (strcmp(arg, "--zygote") == 0) {
            zygote = true;
            niceName = ZYGOTE_NICE_NAME;
        } else if (strcmp(arg, "--start-system-server") == 0) {
            startSystemServer = true;
        } else if (strcmp(arg, "--application") == 0) {
            application = true;
        } else if (strncmp(arg, "--nice-name=", 12) == 0) {
            niceName.setTo(arg + 12);
        } else if (strncmp(arg, "--", 2) != 0) {
            className.setTo(arg);
            break;
        } else {
            --i;
            break;
        }
    }

    Vector<String8> args;
    if (!className.isEmpty()) {
        // We're not in zygote mode, the only argument we need to pass
        // to RuntimeInit is the application argument.
        //
        // The Remainder of args get passed to startup class main(). Make
        // copies of them before we overwrite them with the process name.
        args.add(application ? String8("application") : String8("tool"));
        runtime.setClassNameAndArgs(className, argc - i, argv + i);
    } else {
        // We're in zygote mode.
        maybeCreateDalvikCache();

        if (startSystemServer) {
            args.add(String8("start-system-server"));
        }

        char prop[PROP_VALUE_MAX];
        if (property_get(ABI_LIST_PROPERTY, prop, NULL) == 0) {
            LOG_ALWAYS_FATAL("app_process: Unable to determine ABI list from property %s.",
                ABI_LIST_PROPERTY);
            return 11;
        }

        String8 abiFlag("--abi-list=");
        abiFlag.append(prop);
        args.add(abiFlag);

        // In zygote mode, pass all remaining arguments to the zygote
        // main() method.
        for (; i < argc; ++i) {
            args.add(String8(argv[i]));
        }
    }

    if (!niceName.isEmpty()) {
        runtime.setArgv0(niceName.string(), true /* setProcName */);
    }

    if (zygote) {
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
    } else if (className) {
        runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
    } else {
        fprintf(stderr, "Error: no class name or --zygote supplied.\n");
        app_usage();
        LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
        return 10;
    }
}

可以看到app_main会先创建一个AppRuntime,然后用它拉起Zygote。

AndroidRuntime::AndroidRuntime(char* argBlockStart, const size_t argBlockLength) :
        mExitWithoutCleanup(false),
        mArgBlockStart(argBlockStart),
        mArgBlockLength(argBlockLength)
{
    SkGraphics::Init();
    // There is also a global font cache, but its budget is specified by
    // SK_DEFAULT_FONT_CACHE_COUNT_LIMIT and SK_DEFAULT_FONT_CACHE_LIMIT.

    // Pre-allocate enough space to hold a fair number of options.
    mOptions.setCapacity(20);

    assert(gCurRuntime == NULL);        // one per process
    gCurRuntime = this;
}

在app_main.cpp的最后几行代码可以看到,启动Zygote是交给AndroidRuntime去完成的:

runtime.start(“com.android.internal.os.ZygoteInit”, args, zygote);

在Runtime的start函数中包含了大量对args的检查,具体可以看AndroidRuntime::startVm这个函数。

至此ZygoteInit.main(),就会被执行。

ZygoteInit.main()

系统核心进程,一切系统服务的开始。

main函数大概完成以下操作:

  1. 开启DDMS服务
  2. 读取传递过来的参数(是否启动系统服务等)
  3. 注册Zygote和其他进程通信的Socket
  4. 预加载进程的公共资源到共享内存(主要包含framework相关的基础类以及resource资源)
    • 加快应用启动速度
    • 通过共享内存的方式节约内存(Linux中,父进程已加载的内容可以在子进程中共享)
  5. 启动system_server
  6. 通过死循环开启Loop,从之前注册Socket中读取信息,完成Process的Fork

系统进程创建应用进程

上面提到ZygoteInit创建的时候会注册Socket用于监听其他进程Fork的需求,下面从源码开看看是如何创建出另外进程的:

注册Socket

private static void registerZygoteSocket(String socketName) {
    if (sServerSocket == null) {
        int fileDesc;
        final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
        try {
            String env = System.getenv(fullSocketName);
            fileDesc = Integer.parseInt(env);
        } catch (RuntimeException ex) {
            throw new RuntimeException(fullSocketName + " unset or invalid", ex);
        }

        try {
            FileDescriptor fd = new FileDescriptor();
            fd.setInt$(fileDesc);
            sServerSocket = new LocalServerSocket(fd);
        } catch (IOException ex) {
            throw new RuntimeException(
                    "Error binding to local socket '" + fileDesc + "'", ex);
        }
    }
}

在Socket通信一文中,提到Socket是通过初始化的时候传入的描述符确定使用的那种协议的,在这里也有体现。

需要注意的是,LocalServerSocket一旦初始化就开始监听。

public LocalServerSocket(FileDescriptor fd) throws IOException
{
    impl = new LocalSocketImpl(fd);
    impl.listen(LISTEN_BACKLOG);//开始监听
    localAddress = impl.getSockAddress();
}

轮询是否有新的消息并Fork进程

保证ZygoteInit一直存活的轮询:

private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
    ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
    ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();

    fds.add(sServerSocket.getFileDescriptor());
    peers.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 = (short) POLLIN;
        }
        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 {
                boolean done = peers.get(i).runOnce();
                if (done) {
                    peers.remove(i);
                    fds.remove(i);
                }
            }
        }
    }
}
  • 如果没有已创建的链接需要处理了,那么调用ZygoteInit.acceptCommandPeer()创建新的链接(ZygoteConnection)并调用sServerSocket.accept()获取数据。
  private static ZygoteConnection acceptCommandPeer(String abiList) {
      try {
          return new ZygoteConnection(sServerSocket.accept(), abiList);
      } catch (IOException ex) {
          throw new RuntimeException(
                  "IOException during accept()", ex);
      }
  }
  • 如果还有,那么调用ZygoteConnection.runOnce()。
    boolean runOnce() throws ZygoteInit.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;
      }
    
      if (args == null) {
          // EOF reached.
          closeSocket();
          return true;
      }
    
      /** the stderr of the most recent request, if avail */
      PrintStream newStderr = null;
    
      if (descriptors != null && descriptors.length >= 3) {
          newStderr = new PrintStream(
                  new FileOutputStream(descriptors[2]));
      }
    
      int pid = -1;
      FileDescriptor childPipeFd = null;
      FileDescriptor serverPipeFd = null;
    
      try {
          parsedArgs = new Arguments(args);
    
          if (parsedArgs.abiListQuery) {
              return handleAbiListQuery();
          }
    
          if (parsedArgs.permittedCapabilities != 0 || parsedArgs.effectiveCapabilities != 0) {
              throw new ZygoteSecurityException("Client may not specify capabilities: " +
                      "permitted=0x" + Long.toHexString(parsedArgs.permittedCapabilities) +
                      ", effective=0x" + Long.toHexString(parsedArgs.effectiveCapabilities));
          }
    
          applyUidSecurityPolicy(parsedArgs, peer);
          applyInvokeWithSecurityPolicy(parsedArgs, peer);
    
          applyDebuggerSystemProperty(parsedArgs);
          applyInvokeWithSystemProperty(parsedArgs);
    
          int[][] rlimits = null;
    
          if (parsedArgs.rlimits != null) {
              rlimits = parsedArgs.rlimits.toArray(intArray2d);
          }
    
          if (parsedArgs.invokeWith != null) {
              FileDescriptor[] pipeFds = Os.pipe2(O_CLOEXEC);
              childPipeFd = pipeFds[1];
              serverPipeFd = pipeFds[0];
              Os.fcntlInt(childPipeFd, F_SETFD, 0);
          }
    
          /**
           * In order to avoid leaking descriptors to the Zygote child,
           * the native code must close the two Zygote socket descriptors
           * in the child process before it switches from Zygote-root to
           * the UID and privileges of the application being launched.
           *
           * In order to avoid "bad file descriptor" errors when the
           * two LocalSocket objects are closed, the Posix file
           * descriptors are released via a dup2() call which closes
           * the socket and substitutes an open descriptor to /dev/null.
           */
    
          int [] fdsToClose = { -1, -1 };
    
          FileDescriptor fd = mSocket.getFileDescriptor();
    
          if (fd != null) {
              fdsToClose[0] = fd.getInt$();
          }
    
          fd = ZygoteInit.getServerSocketFileDescriptor();
    
          if (fd != null) {
              fdsToClose[1] = fd.getInt$();
          }
    
          fd = null;
    
          pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
                  parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
                  parsedArgs.niceName, fdsToClose, parsedArgs.instructionSet,
                  parsedArgs.appDataDir);
      } catch (ErrnoException ex) {
          logAndPrintError(newStderr, "Exception creating pipe", ex);
      } catch (IllegalArgumentException ex) {
          logAndPrintError(newStderr, "Invalid zygote arguments", ex);
      } catch (ZygoteSecurityException ex) {
          logAndPrintError(newStderr,
                  "Zygote security policy prevents request: ", ex);
      }
    
      try {
          if (pid == 0) {
              // in child
              IoUtils.closeQuietly(serverPipeFd);
              serverPipeFd = null;
              handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
    
              // should never get here, the child is expected to either
              // throw ZygoteInit.MethodAndArgsCaller or exec().
              return true;
          } else {
              // in parent...pid of < 0 means failure
              IoUtils.closeQuietly(childPipeFd);
              childPipeFd = null;
              return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
          }
      } finally {
          IoUtils.closeQuietly(childPipeFd);
          IoUtils.closeQuietly(serverPipeFd);
      }
    }
    

    最终会调用Zygote.forkAndSpecialize完成进程的创建。

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

    最终调用的是native的方法完成进程的fork。