Android5.0:zygote

zygote本身是一个Linux的Native应用程序,当init进程启动完毕之后,通过init.rc文件启动zygote并修改进程名字为zygote(frameworks/base/cmds/app_process/app_main.cpp文件)。它是一个deamon进程,启动VM,加载class和resource等。之后唯一的任务就是监听socket(/dev/socket/zygote这个是用来监听的socket)并启动application,所有APP的父进程皆为zygote进程。
zygote从”/dev/socket/zygote”监听到APP启动的请求之后,最终会fork()一个子进程。我们知道fork()会完全复制父进程(clone)到另外的一个用户空间。当zygote进行fork之后,还需要创建新的Dalvik VM,还需要preload那些APP需要的class和resource。看起来像是zygote每次启动一个APP的时候都要重新复制整块的内存并重新加载class和resouce,但其实不然,这是因为Linux内核实现的Copy-On-Write(COW)。有Copy-On-Write,意味着在fork复制内存操作中,没有内存是会被马上拷贝的。而是在进程在改写这段内存的时候,kernel会中断这种写操作并先做之前没有做的复制操作。在zygote进行preload的class和resource都是只读的文件,所以在启动APP和运行APP的过程中,没有class和resource是会被复制到另外的内存的,而是所有的APP都在用同一份class和resource,也就是zygote启动的时候加载的那些。

zygote进程的启动

1.设置init.xx.rc启动运行相关service

//system/core/rootdir/init.zygotexx.rc //xx是64或者32,看平台
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    class main
    socket zygote stream 660 root system 
    //设置zygote相关的socket名字,权限等。会生成/dev/socket/zygote节点,权限如下:
    //srw-rw---- root     system      2012-01-28 13:39 zygote
    onrestart write /sys/android_power/request_state wake //??
    onrestart write /sys/power/state on //??
    onrestart restart media
    onrestart restart netd
    onrestart restart loc_launcher

2.init进程读到上面的service之后,会跑到/frameworks/base/cmds/app_process/app_main.cpp文件的main函数,并传入参数。main函数主要做以下几件事情
1)修改当前进程名字为zygote

if (!niceName.isEmpty()) {
    runtime.setArgv0(niceName.string());
    set_process_name(niceName.string());
}
//通过这个最终用prctl()系统调用修改当前进程的名字为zygo

2)调用AppRuntime.start函数!! 下面会继续分析AppRuntime.start函数

AppRuntime.start()函数

AppRuntime.start函数做如下一些事情
1) startVm()初始化DVM

2) startReg()注册JNI函数

3)

char* slashClassName = toSlashClassName(className);
jclass startClass = env->FindClass(slashClassName);
jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
            "([Ljava/lang/String;)V");

env->CallStaticVoidMethod(startClass, startMeth, strArray)
//加载"com.android.internal.os.ZygoteInit"类并调用其main函数。
//第一个java函数调用开始了~~
zygoteinit.java的main()函数

ZygoteInit.main函数主要做以下几件事情
1.调用registerZygoteSocket函数创建了一个socket接口,用来和ActivityManagerService通讯

boolean startSystemServer = false;
String socketName = "zygote";
registerZygoteSocket(socketName);

registerZygoteSocket()函数如下:

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 {
            sServerSocket = new LocalServerSocket(
                createFileDescriptor(fileDesc));
        } catch (IOException ex) {
            throw new RuntimeException(
                "Error binding to local socket '" + fileDesc + "'", ex);
        }
    }
}

可以看到registerZygoteSocket()函数通过叫fullSocketName的环境变量得到env并转换为文件描述符。
这个环境变量的名字可以从上面的函数中知道是”ANDROID_SOCKET_zygote”。这个环境变量是在哪里设置的呢?
我们知道,系统启动脚本文件system/core/rootdir/init.rc是由init进程来解释执行的,而init进程的源代码位于system/core/init目录中,在init.c文件中,是由service_start函数来解释init.rc文件中的service命令的。每一个service命令都会促使init进程调用fork函数来创建一个新的进程,在新的进程里面,会分析里面的socket选项,对于每一个socket选项,都会通过create_socket函数来在/dev/socket目录下创建一个文件,在这个场景中,这个文件便是zygote了,然后得到的文件描述符通过publish_socket函数写入到环境变量中去。publish_socke()函数中可以看到是如何设置环境变量的。

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

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

2.预加载

static void preload() {  
        Log.d(TAG, "begin preload");  
        preloadClasses();  
        preloadResources();  
        preloadOpenGL();  
        preloadSharedLibraries();  
        // Ask the WebViewFactory to do any initialization that must run in the zygote process,  
        // for memory sharing purposes.  
        WebViewFactory.prepareWebViewInZygote();  
        Log.d(TAG, "end preload");  
    }  

3.调用startSystemServer函数来启动SystemServer组件

private static boolean startSystemServer(String abiList, String socketName)
            throws MethodAndArgsCaller, RuntimeException {
    long capabilities = posixCapabilitiesAsBits(
            OsConstants.CAP_BLOCK_SUSPEND,
            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[] = {//SystemServer进程的uid,gid等等,,很多参数设置,需要仔细看看
        "--setuid=1000",
        "--setgid=1000",
        //WTL_EDM_START
        // "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1032,3001,3002,3003,3006,3007",
            //Adds system to net_raw and net_admin (3004, 3005)
            //groups, so that system can use iptables.

"-- setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1032,3001,3002,3003,3004,3005,3006,3007",
    //WTL_EDM_END
    "--capabilities=" + capabilities + "," + capabilities,
    "--runtime-init",
    "--nice-name=system_server",
    "com.android.server.SystemServer",
    };

    ZygoteConnection.Arguments parsedArgs = null;
    int pid;

    try {
        //按照上面的参数赋值给parsedArgs
        parsedArgs = new ZygoteConnection.Arguments(args);
        ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
        ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);

        //fork出来一个SystemServer进程
        /* 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) {
        if (hasSecondZygote(abiList)) {
            waitForSecondaryZygote(socketName);
        }
        //SystemServer进程跑到这里,开始跑handleSystemServerProcess()。
        handleSystemServerProcess(parsedArgs);
    }
    return true;
}
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.  
        Os.umask(S_IRWXG | S_IRWXO);  

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

        final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");  
        if (systemServerClasspath != null) {  
            performSystemServerDexOpt(systemServerClasspath);  
        }  

        if (parsedArgs.invokeWith != null) {  
            String[] args = parsedArgs.remainingArgs;  
            // If we have a non-null system server class path, we'll have to duplicate the  
            // existing arguments and append the classpath to it. ART will handle the classpath  
            // correctly when we exec a new process.  
            if (systemServerClasspath != null) {  
                String[] amendedArgs = new String[args.length + 2];  
                amendedArgs[0] = "-cp";  
                amendedArgs[1] = systemServerClasspath;  
                System.arraycopy(parsedArgs.remainingArgs, 0, amendedArgs, 2, parsedArgs.remainingArgs.length);  
            }  

            WrapperInit.execApplication(parsedArgs.invokeWith,  
                    parsedArgs.niceName, parsedArgs.targetSdkVersion,  
                    null, args);  
        } else {  
            ClassLoader cl = null;  
            if (systemServerClasspath != null) {  
                cl = new PathClassLoader(systemServerClasspath, ClassLoader.getSystemClassLoader());  
                Thread.currentThread().setContextClassLoader(cl);  
            }  

            /* 
             * Pass the remaining arguments to SystemServer. 
             */  
            RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);  
        }  

        /* should never reach here */  
    }  

这里重点是RuntimeInit.zygoteInit()函数

public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader 
    classLoader) throws ZygoteInit.MethodAndArgsCaller {

    if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote");

    redirectLogStreams();
    commonInit();
    nativeZygoteInit();

    applicationInit(targetSdkVersion, argv, classLoader);
}

这个函数都是做一些初始化工作,其中nativeZygoteInit最终会调用到app_main.cpp中的onZygoteInit。

virtual void onZygoteInit()
{
    // Re-enable tracing now that we're no longer in Zygote.
    atrace_set_tracing_enabled(true);

    sp<ProcessState> proc = ProcessState::self();
    ALOGV("App process: starting thread pool.\n");
    proc->startThreadPool();
}

applicationInit函数中的invokeStaticMain(args.startClass, args.startArgs, classLoader)最终会调用SystemServer.java中的main函数。ZygoteInit.java的startSystemServer()函数中,args参数的最后一个就是 “com.android.server.SystemServer”,所以最后调用的就是这个库的main函数。

/**
 * The main entry point from zygote.
 * */
public static void main(String[] args) {
    new SystemServer().run();
}

这里看到SystemServer启动,但具体内容很多,需要后面再进行分析。

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

runSelectLoop()->ZygoteConnection.runOnce()函数负责接收command并启动APP

/*runSelectLoop()函数中,GC_LOOP_COUNT表示跑起来几个APP之后调用一次gc()清理内存。
如果内存较小,可以适当调整这个值?*/

从这里可以再进一步看看ActivityManagerService是怎么组织参数发给zygote来启动一个参数的。
这里也涉及到安全等,需要仔细检查一下。

//安全相关的
applyUidSecurityPolicy(parsedArgs, peer, peerSecurityContext);
applyRlimitSecurityPolicy(parsedArgs, peer, peerSecurityContext);
applyInvokeWithSecurityPolicy(parsedArgs, peer, peerSecurityContext);
applyseInfoSecurityPolicy(parsedArgs, peer, peerSecurityContext);
//需要设置的uid,gid等,,
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
                    parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, 
                    parsedArgs.seInfo,parsedArgs.category, parsedArgs.accessInfo, 
                    parsedArgs.niceName, fdsToClose, parsedArgs.instructionSet,
                    parsedArgs.appDataDir);

至此,zygote进程的启动分析就结束了,它会一直循环在这里,等待其他进程请求它孵化出新的进程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值