Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android5.0:zygote

Android5.0:zygote

編輯:關於Android編程

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 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進程的啟動分析就結束了,它會一直循環在這裡,等待其他進程請求它孵化出新的進程。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved