Android Zygote进程(二)

简介: 学习笔记

进入到ZygoteInit.java#preload()预加载

预加载是指在zygote进程启动的时候就加载,这样系统只在zygote执行一次加载操作,所有APP用到该资源不需要再重新加载,减少资源加载时间,加快了应用启动速度,一般情况下,系统中App共享的资源会被列为预加载资源。

zygote fork子进程时,根据fork的copy-on-write(写时拷贝)机制可知,有些类如果不做改变,甚至都不用复制,子进程可以和父进程共享这部分数据,从而省去不少内存的占用。

预加载的原理:

zygote进程启动后将资源读取出来,保存到Resources一个全局静态变量中,下次读取系统资源的时候优先从静态变量中查找。

/android/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

staticvoidpreload(TimingsTraceLog bootTimingsTraceLog) {

    Log.d(TAG, "begin preload");

    bootTimingsTraceLog.traceBegin("BeginPreload");

    beginPreload();//获取字符集转换资源等

    bootTimingsTraceLog.traceEnd(); // BeginPreload

    bootTimingsTraceLog.traceBegin("PreloadClasses");

    //预加载类的列表---/system/etc/preloaded-classes, 在/frameworks/base/config/preloaded-classes 中,Android10.0中预计有7603左右个类

    preloadClasses();

    bootTimingsTraceLog.traceEnd(); // PreloadClasses

    bootTimingsTraceLog.traceBegin("CacheNonBootClasspathClassLoaders");

    cacheNonBootClasspathClassLoaders();

    bootTimingsTraceLog.traceEnd(); // CacheNonBootClasspathClassLoaders

    bootTimingsTraceLog.traceBegin("PreloadResources");

    //加载图片、颜色等资源文件,部分定义在 /frameworks/base/core/res/res/values/arrays.xml中

    preloadResources();

    bootTimingsTraceLog.traceEnd(); // PreloadResources

    Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadAppProcessHALs");

    nativePreloadAppProcessHALs();

    Trace.traceEnd(Trace.TRACE_TAG_DALVIK);

    Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadGraphicsDriver");

    maybePreloadGraphicsDriver();

    Trace.traceEnd(Trace.TRACE_TAG_DALVIK);

    // 加载 android、compiler_rt、jnigraphics等library

    preloadSharedLibraries();

    //用于初始化文字资源

    preloadTextResources();

    // Ask the WebViewFactory to do any initialization that must run in the zygote process,

    // for memory sharing purposes.

    //用于初始化webview;

    WebViewFactory.prepareWebViewInZygote();

    //预加载完成,可以查看下面的log

    endPreload();

    warmUpJcaProviders();

    Log.d(TAG, "end preload");

 

    sPreloadComplete = true;

}

进入到ZygoteServer.java#ZygoteServer(boolean isPrimaryZygote)

说明:ZygoteServer 构造函数初始化时,根据传入的参数,利用LocalServerSocket (createManagedSocketFromInitSocket()方法中调用)创建了1个本地服务端的socket,用来建立连接。

/android/frameworks/base/core/java/com/android/internal/os/ZygoteServer.java

//创建zygote的socket

ZygoteServer(booleanisPrimaryZygote) {

    mUsapPoolEventFD = Zygote.getUsapPoolEventFD();

 

    if(isPrimaryZygote) {

        //创建socket,并获取socket对象,socketname:zygote

        mZygoteSocket = Zygote.createManagedSocketFromInitSocket(Zygote.PRIMARY_SOCKET_NAME);

        mUsapPoolSocket =

                Zygote.createManagedSocketFromInitSocket(

                        Zygote.USAP_POOL_PRIMARY_SOCKET_NAME);

    else{

        //socketname:zygote_secondary

        mZygoteSocket = Zygote.createManagedSocketFromInitSocket(Zygote.SECONDARY_SOCKET_NAME);

        mUsapPoolSocket =

                Zygote.createManagedSocketFromInitSocket(

                        Zygote.USAP_POOL_SECONDARY_SOCKET_NAME);

    }

 

    mUsapPoolSupported = true;

    fetchUsapPoolPolicyProps();

}

进入到ZygoteInit.java#forkSystemServer()

/android/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

privatestaticRunnable forkSystemServer(String abiList, String socketName,

        ZygoteServer zygoteServer) {

    longcapabilities = posixCapabilitiesAsBits(

            OsConstants.CAP_IPC_LOCK,

            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_PTRACE,

            OsConstants.CAP_SYS_TIME,

            OsConstants.CAP_SYS_TTY_CONFIG,

            OsConstants.CAP_WAKE_ALARM,

            OsConstants.CAP_BLOCK_SUSPEND

    );

    ....

    //准备参数

    /* 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,1021,1023,"

                    "1024,1032,1065,3001,3002,3003,3006,3007,3009,3010,3011",

            "--capabilities="+ capabilities + ","+ capabilities,

            "--nice-name=system_server",

            "--runtime-args",

            "--target-sdk-version="+ VMRuntime.SDK_VERSION_CUR_DEVELOPMENT,

            "com.android.server.SystemServer",

    };

    ZygoteArguments parsedArgs;

 

    intpid;

 

    try{

        ZygoteCommandBuffer commandBuffer = newZygoteCommandBuffer(args);

        try{

            //将上面准备的参数,按照ZygoteArguments的风格进行封装

            parsedArgs = ZygoteArguments.getInstance(commandBuffer);

        catch(EOFException e) {

            thrownewAssertionError("Unexpected argument error for forking system server", e);

        }

        commandBuffer.close();

        Zygote.applyDebuggerSystemProperty(parsedArgs);

        Zygote.applyInvokeWithSystemProperty(parsedArgs);

 

    .....

        //通过fork“分裂”出子进程system_server

        /* Request to fork the system server process */

        pid = Zygote.forkSystemServer(

                parsedArgs.mUid, parsedArgs.mGid,

                parsedArgs.mGids,

                parsedArgs.mRuntimeFlags,

                null,

                parsedArgs.mPermittedCapabilities,

                parsedArgs.mEffectiveCapabilities);

    catch(IllegalArgumentException ex) {

        thrownewRuntimeException(ex);

    }

 

    //进入子进程system_server

    /* For child process */

    if(pid == 0) {

        // 处理32_64和64_32的情况

        if(hasSecondZygote(abiList)) {

            waitForSecondaryZygote(socketName);

        }

        // fork时会copy socket,system server需要主动关闭

        zygoteServer.closeServerSocket();

        // system server进程处理自己的工作

        returnhandleSystemServerProcess(parsedArgs);

    }

 

    returnnull;

}

主要时调用了Zygote.java#forkSystemServer()方法,在这个方法中调用了Native的方法,但最后会调用fork()方法创建子进程。

fork()函数得到的子进程是父进程的一个复制品,通过写时拷贝实现。

fork调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次。

返回值:

  • 返回0:表示成功创建子进程,并且接下来进入子进程执行liuc
  • 返回>0:表示成功创建子进程,并且急促执行父进程流程代码
  • 返回非正数<0:表示创建子进程失败,失败的原因主要有:进程数超过了系统所能创建的上线,errno会被设置为EAGAIN系统内存不足,errno会被设置为ENOMEM

回到ZygoteServer()方法中,在新fork出的子进程中调用了handleSystemServerProcess(),主要是返回Runtime.java的MethodAndArgsCaller的方法,然后通过r.run() 启动com.android.server.SystemServer的main 方法。

这个当我们后面的SystemServer的章节进行详细讲解。

handleSystemServerProcess代码流程:

handleSystemServerProcess()

    |

    [ZygoteInit.java]

    zygoteInit()

        |

    [RuntimeInit.java]

    applicationInit()

        |

    findStaticMain()

        |

    MethodAndArgsCaller()

进入ZygoteServer.java#runSelectLoop()

说明:使zygote进程进入无限循环,处理请求

/android/frameworks/base/core/java/com/android/internal/os/ZygoteServer.java

Runnable runSelectLoop(String abiList) {

    ArrayList<FileDescriptor> socketFDs = newArrayList<>();

    ArrayList<ZygoteConnection> peers = newArrayList<>();

 

    //将server socket加入到fds

    socketFDs.add(mZygoteSocket.getFileDescriptor());

    peers.add(null);

 

    mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;

 

    while(true) {

        fetchUsapPoolPolicyPropsWithMinInterval();

        mUsapPoolRefillAction = UsapPoolRefillAction.NONE;

 

        int[] usapPipeFDs = null;

        StructPollfd[] pollFDs;

        //每次循环,都重新创建需要监听的pollFds

        if(mUsapPoolEnabled) {

            usapPipeFDs = Zygote.getUsapPipeFDs();

            pollFDs = newStructPollfd[socketFDs.size() + 1+ usapPipeFDs.length];

        else{

            pollFDs = newStructPollfd[socketFDs.size()];

        }

 

        intpollIndex = 0;

        for(FileDescriptor socketFD : socketFDs) {

            //关注事件到来

            pollFDs[pollIndex] = newStructPollfd();

            pollFDs[pollIndex].fd = socketFD;

            pollFDs[pollIndex].events = (short) POLLIN;

            ++pollIndex;

        }

 

        finalintusapPoolEventFDIndex = pollIndex;

 

        if(mUsapPoolEnabled) {

            pollFDs[pollIndex] = newStructPollfd();

            pollFDs[pollIndex].fd = mUsapPoolEventFD;

            pollFDs[pollIndex].events = (short) POLLIN;

            ++pollIndex;

 

            // The usapPipeFDs array will always be filled in if the USAP Pool is enabled.

            assertusapPipeFDs != null;

            for(intusapPipeFD : usapPipeFDs) {

                FileDescriptor managedFd = newFileDescriptor();

                managedFd.setInt$(usapPipeFD);

 

                pollFDs[pollIndex] = newStructPollfd();

                pollFDs[pollIndex].fd = managedFd;

                pollFDs[pollIndex].events = (short) POLLIN;

                ++pollIndex;

            }

        }

 

        intpollTimeoutMs;

 

        if(mUsapPoolRefillTriggerTimestamp == INVALID_TIMESTAMP) {

            pollTimeoutMs = -1;

        else{

            longelapsedTimeMs = System.currentTimeMillis() - mUsapPoolRefillTriggerTimestamp;

 

            if(elapsedTimeMs >= mUsapPoolRefillDelayMs) {

                // The refill delay has elapsed during the period between poll invocations.

                // We will now check for any currently ready file descriptors before refilling

                // the USAP pool.

                pollTimeoutMs = 0;

                mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;

                mUsapPoolRefillAction = UsapPoolRefillAction.DELAYED;

 

            elseif(elapsedTimeMs <= 0) {

                // This can occur if the clock used by currentTimeMillis is reset, which is

                // possible because it is not guaranteed to be monotonic.  Because we can't tell

                // how far back the clock was set the best way to recover is to simply re-start

                // the respawn delay countdown.

                pollTimeoutMs = mUsapPoolRefillDelayMs;

 

            else{

                pollTimeoutMs = (int) (mUsapPoolRefillDelayMs - elapsedTimeMs);

            }

        }

 

        intpollReturnValue;

        try{

            //等待事件到来

            pollReturnValue = Os.poll(pollFDs, pollTimeoutMs);

        catch(ErrnoException ex) {

            thrownewRuntimeException("poll failed", ex);

        }

 

        if(pollReturnValue == 0) {

            // The poll returned zero results either when the timeout value has been exceeded

            // or when a non-blocking poll is issued and no FDs are ready.  In either case it

            // is time to refill the pool.  This will result in a duplicate assignment when

            // the non-blocking poll returns zero results, but it avoids an additional

            // conditional in the else branch.

            mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;

            mUsapPoolRefillAction = UsapPoolRefillAction.DELAYED;

 

        else{

            booleanusapPoolFDRead = false;

            //倒序处理,即优先处理已建立链接的信息,后处理新建链接的请求

            while(--pollIndex >= 0) {

                if((pollFDs[pollIndex].revents & POLLIN) == 0) {

                    continue;

                }

 

                //server socket最先加入fds,因此这里是server socket收到数据

                if(pollIndex == 0) {

                    // Zygote server socket

                    //收到新的建立通信的请求,建立通信连接

                    ZygoteConnection newPeer = acceptCommandPeer(abiList);

                    //加入到peers和fds,即下一次也开监听

                    peers.add(newPeer);

                    socketFDs.add(newPeer.getFileDescriptor());

                elseif(pollIndex < usapPoolEventFDIndex) {

                    // Session socket accepted from the Zygote server socket

                    //说明接收AMS发送过来创建应用程序的请求,调用processOneCommand来创建新的应用程序进程

                    try{

                        //有socket连接,创建ZygoteConnection对象,并添加到fds

                        ZygoteConnection connection = peers.get(pollIndex);

                        booleanmultipleForksOK = !isUsapPoolEnabled()

                                && ZygoteHooks.isIndefiniteThreadSuspensionSafe();

                        //处理连接

                        finalRunnable command =

                                connection.processCommand(this, multipleForksOK);

 

                        // TODO (chriswailes): Is this extra check necessary?

                        if(mIsForkChild) {

                            // We're in the child. We should always have a command to run at

                            // this stage if processCommand hasn't called "exec".

                            if(command == null) {

                                thrownewIllegalStateException("command == null");

                            }

 

                            returncommand;

                        else{

                            // We're in the server - we should never have any commands to run.

                            if(command != null) {

                                thrownewIllegalStateException("command != null");

                            }

 

                            // We don't know whether the remote side of the socket was closed or

                            // not until we attempt to read from it from processCommand. This

                            // shows up as a regular POLLIN event in our regular processing

                            // loop.

                            if(connection.isClosedByPeer()) {

                                connection.closeSocket();

                                peers.remove(pollIndex);

                                socketFDs.remove(pollIndex);//处理完则从fds中移除该文件描述符

                            }

                        }

                    catch(Exception e) {

                        ......

                    finally{

                        // Reset the child flag, in the event that the child process is a child-

                        // zygote. The flag will not be consulted this loop pass after the

                        // Runnable is returned.

                        mIsForkChild = false;

                    }

 

                }

                    ......

}

processOneCommand()

/android/frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java

Runnable processCommand(ZygoteServer zygoteServer, booleanmultipleOK) {

.....

            if(parsedArgs.mInvokeWith != null|| parsedArgs.mStartChildZygote

                    || !multipleOK || peer.getUid() != Process.SYSTEM_UID) {

                // Continue using old code for now. TODO: Handle these cases in the other path.

                //fork子进程

                pid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid,

                        parsedArgs.mGids, parsedArgs.mRuntimeFlags, rlimits,

                        parsedArgs.mMountExternal, parsedArgs.mSeInfo, parsedArgs.mNiceName,

                        fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,

                        parsedArgs.mInstructionSet, parsedArgs.mAppDataDir,

                        parsedArgs.mIsTopApp, parsedArgs.mPkgDataInfoList,

                        parsedArgs.mAllowlistedDataInfoList, parsedArgs.mBindMountAppDataDirs,

                        parsedArgs.mBindMountAppStorageDirs);

 

                try{

                    if(pid == 0) {

                        // in child

                        //子进程执行

                        zygoteServer.setForkChild();

    

                        zygoteServer.closeServerSocket();

                        IoUtils.closeQuietly(serverPipeFd);

                        serverPipeFd = null;

                        //进入子进程流程

                        returnhandleChildProc(parsedArgs, childPipeFd,

                                parsedArgs.mStartChildZygote);

                    else{

                        // In the parent. A pid < 0 indicates a failure and will be handled in

                        // handleParentProc.

                        //父进程执行

                        IoUtils.closeQuietly(childPipeFd);

                        childPipeFd = null;

                        handleParentProc(pid, serverPipeFd);

                        returnnull;

                    }

                }

        .....

}

handleChildProc()

/android/frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java

privateRunnable handleChildProc(ZygoteArguments parsedArgs,

        FileDescriptor pipeFd, booleanisZygote) {

 

    closeSocket();

 

    Zygote.setAppProcessName(parsedArgs, TAG);

 

    // End of the postFork event.

    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

    if(parsedArgs.mInvokeWith != null) {

        WrapperInit.execApplication(parsedArgs.mInvokeWith,

                parsedArgs.mNiceName, parsedArgs.mTargetSdkVersion,

                VMRuntime.getCurrentInstructionSet(),

                pipeFd, parsedArgs.mRemainingArgs);

 

        // Should not get here.

        thrownewIllegalStateException("WrapperInit.execApplication unexpectedly returned");

    else{

        if(!isZygote) {

            //App进程将会调用到这里,执行目标类的main()方法

            returnZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,

                    parsedArgs.mDisabledCompatChanges,

                    parsedArgs.mRemainingArgs, null/* classLoader */);

        else{

            returnZygoteInit.childZygoteInit(

                    parsedArgs.mRemainingArgs  /* classLoader */);

        }

    }

}

总结

image.png

  1. 开启虚拟器
  2. 注册了JNI
  3. 通过JNI调用ZygoteInit的main函数进入Zygote的Java框架层
  4. 创建了服务端Socket
  5. 预加载类和资源
  6. 启动SystemServer进程
  7. 通过runSelectLoop函数无限循环等待如AMS等的请求
相关文章
|
2月前
|
Java Android开发 数据安全/隐私保护
Android中多进程通信有几种方式?需要注意哪些问题?
本文介绍了Android中的多进程通信(IPC),探讨了IPC的重要性及其实现方式,如Intent、Binder、AIDL等,并通过一个使用Binder机制的示例详细说明了其实现过程。
273 4
|
3月前
|
API Android开发
Android P 性能优化:创建APP进程白名单,杀死白名单之外的进程
本文介绍了在Android P系统中通过创建应用进程白名单并杀死白名单之外的进程来优化性能的方法,包括设置权限、获取运行中的APP列表、配置白名单以及在应用启动时杀死非白名单进程的代码实现。
60 1
|
3月前
|
Android开发 开发者 Kotlin
Android 多进程情况下判断应用是否处于前台或者后台
本文介绍在多进程环境下判断Android应用前后台状态的方法。通过`ActivityManager`和服务信息`RunningAppProcessInfo`可有效检测应用状态,优化资源使用。提供Kotlin代码示例,帮助开发者轻松集成。
255 8
|
5月前
|
大数据 Linux Android开发
Android ParcelFileDescriptor实现进程间通信
Android ParcelFileDescriptor实现进程间通信
80 0
|
6月前
|
存储 Java Android开发
Zygote进程启动过程
Zygote进程启动过程
46 1
|
6月前
|
XML 前端开发 Android开发
Android架构设计——MVC(1),Android多进程从头讲到尾
Android架构设计——MVC(1),Android多进程从头讲到尾
|
Java Linux Android开发
理解Android进程创建流程
理解Android进程创建流程
106 0
|
6月前
|
安全 Linux API
Android进程与线程
Android进程与线程
49 0
|
Unix Linux Android开发
Android C++系列:Linux进程间通信(二)
mmap可以把磁盘文件的一部分直接映射到内存,这样文件中的位置直接就有对应的内存 地址,对文件的读写可以直接用指针来做而不需要read/write函数。
107 0
|
Android开发
【Android 逆向】Android 进程注入工具开发 ( 远程调用 | x86 架构的返回值获取 | arm 架构远程调用 )
【Android 逆向】Android 进程注入工具开发 ( 远程调用 | x86 架构的返回值获取 | arm 架构远程调用 )
376 0