欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android 世界中,谁喊醒了 Zygote ?

程序员文章站 2022-04-21 16:39:26
本文基于 Android 9.0 , 代码仓库地址 : "android_9.0.0_r45" 文中源码链接: "SystemServer.java" "ActivityManagerService.java" "Process.java" "ZygoteProcess.java" 对 和 启动流程 ......

本文基于 android 9.0 , 代码仓库地址 :

文中源码链接:

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);
        }
    }

主要进行一些资源清理的工作。到这里,子进程就创建完成了。

总结

Android 世界中,谁喊醒了 Zygote ?

  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 题解。

更多最新原创文章,扫码关注我吧!

Android 世界中,谁喊醒了 Zygote ?