android zygote之启动过程分析

文章出处:http://blog.csdn.net/shift_wwx


前言:之前一篇博文(Android 的init过程详解)中记录了init启动过程,后来另一篇(Android init.rc详解 )中记录了init.rc的解析过程,android详细的启动过程,就需要将所有的init rc文件解剖之后,从action到service挨个分析。这里记录一下zygote的启动过程。


启动命令

init.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 media                                                    
    onrestart restart netd

通过app_process引导启动zygote,/system/bin/app_process 之后的都是命令参数。

接下来的socket关键字表示这个zygote进程需要一个名称为"zygote"的socket资源,这样,系统启动后,我们就可以在/dev/socket目录下看到有一个名为zygote的文件。ActivityManagerService就是通这个socket来和zygote进程通信请求fork一个应用程序进程的了。

最后的一系列onrestart关键字表示这个zygote进程重启时需要执行的命令。

详细的可以参考Android init.rc详解


下面来分析一下source code:

@/frameworks/base/cmds/app_process/app_main.cpp

int main(int argc, char* const argv[])
{
#ifdef __arm__
    /*
     * b/7188322 - Temporarily revert to the compat memory layout
     * to avoid breaking third party apps.
     *
     * THIS WILL GO AWAY IN A FUTURE ANDROID RELEASE.
     *
     * http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=7dbaa466
     * changes the kernel mapping from bottom up to top-down.
     * This breaks some programs which improperly embed
     * an out of date copy of Android's linker.
     */
    char value[PROPERTY_VALUE_MAX];
    property_get("ro.kernel.qemu", value, "");
    bool is_qemu = (strcmp(value, "1") == 0);
    if ((getenv("NO_ADDR_COMPAT_LAYOUT_FIXUP") == NULL) && !is_qemu) {
        int current = personality(0xFFFFFFFF);
        if ((current & ADDR_COMPAT_LAYOUT) == 0) {
            personality(current | ADDR_COMPAT_LAYOUT);
            setenv("NO_ADDR_COMPAT_LAYOUT_FIXUP", "1", 1);
            execv("/system/bin/app_process", argv);
            return -1;
        }
    }
    unsetenv("NO_ADDR_COMPAT_LAYOUT_FIXUP");
#endif

    // These are global variables in ProcessState.cpp
    mArgC = argc;
    mArgV = argv;

    mArgLen = 0;
    for (int i=0; i<argc; i++) {
        mArgLen += strlen(argv[i]) + 1;
    }
    mArgLen--;

    AppRuntime runtime;
    const char* argv0 = argv[0];

    // Process command line arguments
    // ignore argv[0]
    argc--;
    argv++;

    // Everything up to '--' or first non '-' arg goes to the vm

    int i = runtime.addVmArguments(argc, argv);

    // Parse runtime arguments.  Stop at first unrecognized option.
    bool zygote = false;
    bool startSystemServer = false;
    bool application = false;
    const char* parentDir = NULL;
    const char* niceName = NULL;
    const char* className = NULL;

    while (i < argc) {
        const char* arg = argv[i++];
        if (!parentDir) {
            parentDir = arg;
        } else if (strcmp(arg, "--zygote") == 0) {
            zygote = true;
            niceName = "zygote";
        } 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 = arg + 12;
        } else {
            className = arg;
            break;
        }
    }

    if (niceName && *niceName) {
        setArgv0(argv0, niceName);
        set_process_name(niceName);//进程名为zygote
    }

    runtime.mParentDir = parentDir;//没看懂是干嘛的,应该只是做个路径保存吧,/system/bin

    if (zygote) {
        runtime.start("com.android.internal.os.ZygoteInit",
                startSystemServer ? "start-system-server" : "");//启动zygote
    } else if (className) {//除了zygote,RuntimeInit也在这里启动
        // Remainder of args get passed to startup class main()
        runtime.mClassName = className;
        runtime.mArgC = argc - i;
        runtime.mArgV = argv + i;
        runtime.start("com.android.internal.os.RuntimeInit",
                application ? "application" : "tool");
    } 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;
    }
}

刚开始的时候直接忽略了argv[0],直接从命令的路径开始,通过while循环逐个解析命令参数,parentDir、niceName、startSystemServer、zygote等等。

    if (niceName && *niceName) {
        setArgv0(argv0, niceName);
        set_process_name(niceName);
    }
niceName是”zygote“,保存到了argv0,没看懂这个函数内部变量是干什么的?

设置了进程名为zygote。

@/syste/core/libcutils/process_name.c

void set_process_name(const char* new_name) {
#ifdef HAVE_ANDROID_OS
    char  propBuf[PROPERTY_VALUE_MAX];
#endif

    if (new_name == NULL) {
        return;
    }

    // We never free the old name. Someone else could be using it.
    int len = strlen(new_name);
    char* copy = (char*) malloc(len + 1);
    strcpy(copy, new_name);
    process_name = (const char*) copy;

#if defined(HAVE_PRCTL)
    if (len < 16) {
        prctl(PR_SET_NAME, (unsigned long) new_name, 0, 0, 0);
    } else {
        prctl(PR_SET_NAME, (unsigned long) new_name + len - 15, 0, 0, 0);
    }
#endif

#ifdef HAVE_ANDROID_OS
    // If we know we are not running in the emulator, then return.
    if (running_in_emulator == 0) {
        return;
    }

    // If the "running_in_emulator" variable has not been initialized,
    // then do it now.
    if (running_in_emulator == -1) {
        property_get("ro.kernel.qemu", propBuf, "");
        if (propBuf[0] == '1') {
            running_in_emulator = 1;
        } else {
            running_in_emulator = 0;
            return;
        }
    }

    // If the emulator was started with the "-trace file" command line option
    // then we want to record the process name in the trace even if we are
    // not currently tracing instructions (so that we will know the process
    // name when we do start tracing instructions).  We do not need to execute
    // this code if we are just running in the emulator without the "-trace"
    // command line option, but we don't know that here and this function
    // isn't called frequently enough to bother optimizing that case.
    int fd = open(PROCESS_NAME_DEVICE, O_RDWR);
    if (fd < 0)
        return;
    write(fd, process_name, strlen(process_name) + 1);
    close(fd);
#endif
}
后来通过
runtime.start("com.android.internal.os.ZygoteInit",
                startSystemServer ? "start-system-server" : "");
来启动zygote,具体start做了什么可以参考 Dalvik虚拟机的启动过程

runtime.start最终会调用到ZygoteInit.java中的main函数:

    public static void main(String argv[]) {
        try {
            // Start profiling the zygote initialization.
            SamplingProfilerIntegration.start();

            registerZygoteSocket();
            EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
                SystemClock.uptimeMillis());
            if(!SystemProperties.getBoolean("config.enable_quickboot", false)) {
                preload();
            }
            EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
                SystemClock.uptimeMillis());

            // Finish profiling the zygote initialization.
            SamplingProfilerIntegration.writeZygoteSnapshot();

            // Do an initial gc to clean up after startup
            gc();

            // Disable tracing so that forked processes do not inherit stale tracing tags from
            // Zygote.
            Trace.setTracingEnabled(false);

            // If requested, start system server directly from Zygote
            if (argv.length != 2) {
                throw new RuntimeException(argv[0] + USAGE_STRING);
            }

            if (argv[1].equals("start-system-server")) {
                startSystemServer();
            } else if (!argv[1].equals("")) {
                throw new RuntimeException(argv[0] + USAGE_STRING);
            }

            Log.i(TAG, "Accepting command socket connections");

            runSelectLoop();

            closeServerSocket();
        } catch (MethodAndArgsCaller caller) {
            caller.run();
        } catch (RuntimeException ex) {
            Log.e(TAG, "Zygote died with exception", ex);
            closeServerSocket();
            throw ex;
        }
    }
最主要的就是做了三件事情:

1)创建zygote socket,用来和ActivityManagerService通讯

2)startSystemServer启动system_server

3)调用runSelectLoopMode函数进入一个无限循环在前面创建的socket接口上等待ActivityManagerService请求创建新的应用程序进程。


接下来对main函数做简单的分析:

1、数据统计

// Start profiling the zygote initialization.
SamplingProfilerIntegration.start();

......

// Finish profiling the zygote initialization.
SamplingProfilerIntegration.writeZygoteSnapshot();

2、registerZygoteSocket

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

        try {
            sServerSocket = new LocalServerSocket(
                    createFileDescriptor(fileDesc));
        } catch (IOException ex) {
            throw new RuntimeException(
                    "Error binding to local socket '" + fileDesc + "'", ex);
        }
    }
}
这个socket接口是通过文件描述符来创建的,这个文件描符代表的就是我们前面说的/dev/socket/zygote文件了。这个文件描述符是通过环境变量ANDROID_SOCKET_ENV得到的,它定义为:
private static final String ANDROID_SOCKET_ENV = "ANDROID_SOCKET_zygote";
那么,这个环境变量的值又是由谁来设置的呢?我们知道,系统启动脚本文件system/core/rootdir/init.rc是由init进程来解释执行的,而init进程的源代码位于system/core/init目录中,在init.c文件中,是由service_start函数来解释init.rc文件中的service命令的:
void service_start(struct service *svc, const char *dynamic_args)  
{  
    ......  
  
    pid_t pid;  
  
    ......  
  
    pid = fork();  
  
    if (pid == 0) {  
        struct socketinfo *si;  
  
        ......  
  
        for (si = svc->sockets; si; si = si->next) {  
            int socket_type = (  
                !strcmp(si->type, "stream") ? SOCK_STREAM :  
                (!strcmp(si->type, "dgram") ? SOCK_DGRAM : SOCK_SEQPACKET));  
            int s = create_socket(si->name, socket_type,  
                si->perm, si->uid, si->gid);  
            if (s >= 0) {  
                publish_socket(si->name, s);  
            }  
        }  
  
        ......  
    }  
  
    ......  
}
每一个service命令都会促使init进程调用fork函数来创建一个新的进程,在新的进程里面,会分析里面的socket选项,对于每一个socket选项,都会通过create_socket函数来在/dev/socket目录下创建一个文件,在这个场景中,这个文件便是zygote了,然后得到的文件描述符通过publish_socket函数写入到环境变量中去:
static void publish_socket(const char *name, int fd)
{
    char key[64] = ANDROID_SOCKET_ENV_PREFIX;
    char val[64];

    strlcpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1,
            name,
            sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX));
    snprintf(val, sizeof(val), "%d", fd);
    add_environment(key, val);

    /* make sure we don't close-on-exec */
    fcntl(fd, F_SETFD, 0);
}
这里就将init.rc中的zygote传了进来,而ANDROID_SOCKET_ENV_PREFIX在system/core/include/cutils/sockets.h定义为:

#define ANDROID_SOCKET_ENV_PREFIX	"ANDROID_SOCKET_"
#define ANDROID_SOCKET_DIR		"/dev/socket"
因此,这里就把上面得到的文件描述符写入到以"ANDROID_SOCKET_zygote"为key值的环境变量中。又因为上面的ZygoteInit.registerZygoteSocket函数与这里创建socket文件的create_socket函数是运行在同一个进程中,因此,上面的ZygoteInit.registerZygoteSocket函数可以直接使用这个文件描述符来创建一个Java层的LocalServerSocket对象。如果其它进程也需要打开这个/dev/socket/zygote文件来和Zygote进程进行通信,那就必须要通过文件名来连接这个LocalServerSocket了。

3、preload

if(!SystemProperties.getBoolean("config.enable_quickboot", false)) {
    preload();
}
对于preload这里不做过多的介绍,可以参考 android 系统资源的加载和获取

4、startSystemServer

private static boolean startSystemServer()
        throws MethodAndArgsCaller, RuntimeException {
    long capabilities = posixCapabilitiesAsBits(
        OsConstants.CAP_KILL,
        OsConstants.CAP_NET_ADMIN,
        OsConstants.CAP_NET_BIND_SERVICE,
        OsConstants.CAP_NET_BROADCAST,
        OsConstants.CAP_NET_RAW,
        OsConstants.CAP_SYS_MODULE,
        OsConstants.CAP_SYS_NICE,
        OsConstants.CAP_SYS_RESOURCE,
        OsConstants.CAP_SYS_TIME,
        OsConstants.CAP_SYS_TTY_CONFIG
    );
    /* Hardcoded command line to start the system server */
    String args[] = {
        "--setuid=1000",
        "--setgid=1000",
        "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1032,3001,3002,3003,3006,3007",
        "--capabilities=" + capabilities + "," + capabilities,
        "--runtime-init",
        "--nice-name=system_server",
        "com.android.server.SystemServer",
    };
    ZygoteConnection.Arguments parsedArgs = null;

    int pid;

    try {
        parsedArgs = new ZygoteConnection.Arguments(args);
        ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
        ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);

        /* Request to fork the system server process */
        pid = Zygote.forkSystemServer(
                parsedArgs.uid, parsedArgs.gid,
                parsedArgs.gids,
                parsedArgs.debugFlags,
                null,
                parsedArgs.permittedCapabilities,
                parsedArgs.effectiveCapabilities);
    } catch (IllegalArgumentException ex) {
        throw new RuntimeException(ex);
    }

    /* For child process */
    if (pid == 0) {
        handleSystemServerProcess(parsedArgs);
    }

    return true;
}
对于system_server进程,可以参考 android systemserver启动详解
重要的就是pid = Zygote.forkSystemServer(),创建一个新的进程来启动SystemServer组件,返回值pid等0的地方就是新的进程要执行的路径,即新创建的进程会执行handleSystemServerProcess函数。

    private static void handleSystemServerProcess(
            ZygoteConnection.Arguments parsedArgs)
            throws ZygoteInit.MethodAndArgsCaller {

        closeServerSocket();

        // set umask to 0077 so new files and directories will default to owner-only permissions.
        Libcore.os.umask(S_IRWXG | S_IRWXO);

        if (parsedArgs.niceName != null) {
            Process.setArgV0(parsedArgs.niceName);
        }

        if (parsedArgs.invokeWith != null) {
            WrapperInit.execApplication(parsedArgs.invokeWith,
                    parsedArgs.niceName, parsedArgs.targetSdkVersion,
                    null, parsedArgs.remainingArgs);
        } else {
            /*
             * Pass the remaining arguments to SystemServer.
             */
            RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs);
        }

        /* should never reach here */
    }

由于由Zygote进程创建的子进程会继承Zygote进程在前面Step 2中创建的Socket文件描述符,而这里的子进程又不会用到它,因此,这里就调用closeServerSocket函数来关闭它。这个函数接着调用RuntimeInit.zygoteInit函数来进一步执行启动SystemServer组件的操作。详细的可以参考android systemserver启动详解

5、runSelectLoop

    /**
     * Runs the zygote process's select loop. Accepts new connections as
     * they happen, and reads commands from connections one spawn-request's
     * worth at a time.
     *
     * @throws MethodAndArgsCaller in a child process when a main() should
     * be executed.
     */
    private static void runSelectLoop() throws MethodAndArgsCaller {
        ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
        ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
        FileDescriptor[] fdArray = new FileDescriptor[4];

        fds.add(sServerSocket.getFileDescriptor());
        peers.add(null);

        int loopCount = GC_LOOP_COUNT;
        while (true) {
            int index;

            /*
             * Call gc() before we block in select().
             * It's work that has to be done anyway, and it's better
             * to avoid making every child do it.  It will also
             * madvise() any free memory as a side-effect.
             *
             * Don't call it every time, because walking the entire
             * heap is a lot of overhead to free a few hundred bytes.
             */
            if (loopCount <= 0) {//Zygote每循环GC_LOOP_COUNT(这里的值是10)次就会进行一次内存回收</span></span>
                gc();
                loopCount = GC_LOOP_COUNT;
            } else {
                loopCount--;
            }


            try {
                fdArray = fds.toArray(fdArray);
                index = selectReadable(fdArray);//内部由select()实现,在没有客户端事件时会堵塞
            } catch (IOException ex) {
                throw new RuntimeException("Error in select()", ex);
            }

            if (index < 0) {
                throw new RuntimeException("Error in select()");
            } else if (index == 0) {//index==0表示selcet接收到的是Zygote的socket的事件
                ZygoteConnection newPeer = acceptCommandPeer();
                peers.add(newPeer);
                fds.add(newPeer.getFileDesciptor());
            } else {//调用ZygoteConnection对象的runOnce方法,ZygoteConnection是在index == 0时被添加到peers的
                boolean done;
                done = peers.get(index).runOnce();

                if (done) {
                    peers.remove(index);
                    fds.remove(index);
                }
            }
        }
    }
这就是在等待ActivityManagerService来连接这个Socket,然后调用ZygoteConnection.runOnce函数来创建新的应用程序


这样,Zygote进程就启动完成了,学习到这里,我们终于都对Android系统中的进程有了一个深刻的认识了,这里总结一下:

1. 系统启动时init进程会创建Zygote进程,Zygote进程负责后续Android应用程序框架层的其它进程的创建和启动工作。

2. Zygote进程会首先创建一个SystemServer进程,SystemServer进程负责启动系统的关键服务,如包管理服务PackageManagerService和应用程序组件管理服务ActivityManagerService。

 3. 当我们需要启动一个Android应用程序时,ActivityManagerService会通过Socket进程间通信机制,通知Zygote进程为这个应用程序创建一个新的进程。





你可能感兴趣的:(android,SystemServer,init,Zygote,zygoteinit)