当前位置: 移动技术网 > IT编程>移动开发>Android > Android 世界中,谁喊醒了 Zygote ?

Android 世界中,谁喊醒了 Zygote ?

2019年10月16日  | 移动技术网IT编程  | 我要评论

前肾康,漫画派对官网,温梦杰

本文基于 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);
        }
    }

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

总结

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

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

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网