我是怎麼把一個個app進程建立起來的?

前言

古有女媧造人,今有我zygote造進程!java

Linux天字第一號進程【init】把我帶來了這個世界,不甘平凡的我,得有本身的一方天地。因而我自創了本身的一方世界【Java】。雖然有了本身的小天地,但只有本身孤零零一我的,未免太孤獨了,得爲這個世界添加更多的精彩。但憑我一我的幹,豈不累死?二話不說,我用本身做爲本體,克隆了一份本身,並命名爲【system_server】,而後讓他去作一些雜七雜八的事,創造各類各樣的進程服務。我呢,就留下一個後世聯繫個人入口,有須要我孵化後代的事,我再出力,其他時間,都是我happy的時間了~~android

哈哈,YY結束,開始正文!微信

概述

在上一篇文章中,咱們大概清楚了【Zygote】的啓動流程,由init進程解析init.rc文件並啓動,而後【Zygote】fork出了[system_server]進程後,建立了名爲【zygote】的【socket】,並阻塞監聽這個【socket】,響應【AMS】的進程建立請求。那具體這部分是怎麼實現的呢?如下分兩部分進行分析,分別是【AMS】端的建立請求過程和【Zygote】端的響應建立的過程。app

AMS發起進程建立的請求

經過【startActivity】啓動有用時,最終會調用到【AMS】的【startProcess】,咱們的分析就是從這裏開始的。下面先上一張流程圖,方便你們有一個整體的流程感。socket

image

【AMS】的【startProcess】方法實現以下:函數

frameworks/base/services/core/java/com/android/server/am/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) {
          
    ...
    
     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});
    ...
}
複製代碼

這個方法比較簡單,咱們專一咱們所關心的,直接調用【Process】類的【start】方法:oop

frameworks/base/core/java/android/os/Process.java

 public static final 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) {
    return zygoteProcess.start(processClass, niceName, uid, gid, gids,
                runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                abi, instructionSet, appDataDir, invokeWith, zygoteArgs);
}
複製代碼

方法很簡單,只有一句,經過【zygoteProcess】對象,調用它的start方法,【zygoteProcess】是【ZygoteProcess】類的實例化,這裏咱們先看看【zygoteProcess】是怎麼被new出來的:ui

frameworks/base/core/java/android/os/ZygoteProcess.java
 public static final ZygoteProcess zygoteProcess =
        new ZygoteProcess(ZYGOTE_SOCKET, SECONDARY_ZYGOTE_SOCKET);
複製代碼

調用【ZygoteProcess】的構造函數,而且傳入了兩個參數:this

frameworks/base/core/java/android/os/ZygoteProcess.java
public static final String ZYGOTE_SOCKET = "zygote";
public static final String SECONDARY_ZYGOTE_SOCKET = "zygote_secondary";
複製代碼

這裏很重要,傳入兩個參數,都是跟socket相關,且名稱跟【Zygote】相關。這極可能是在爲鏈接【zygote】這個socket作準備。咱們看看【ZygoteProcess】的構造方法實現:spa

frameworks/base/core/java/android/os/ZygoteProcess.java
public ZygoteProcess(String primarySocket, String secondarySocket) {
    this(new LocalSocketAddress(primarySocket, LocalSocketAddress.Namespace.RESERVED),
            new LocalSocketAddress(secondarySocket, LocalSocketAddress.Namespace.RESERVED));
}
複製代碼

構造方法中調用了【this】方法,其實就是另一個構造方法的調用:

public ZygoteProcess(LocalSocketAddress primarySocket, LocalSocketAddress secondarySocket) {
    mSocket = primarySocket;
    mSecondarySocket = secondarySocket;
}
複製代碼

其實也就是:

mSocket = new LocalSocketAddress(primarySocket, LocalSocketAddress.Namespace.RESERVED);
mSecondarySocket = new LocalSocketAddress(secondarySocket, LocalSocketAddress.Namespace.RESERVED;
複製代碼

這樣就獲取了【zygote】的socket入口,後面會經過這個地址去跟【zygote】通信。

咱們繼續看看【ZygoteProcess】的start方法:

frameworks/base/core/java/android/os/ZygoteProcess.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】方法的倒數第二個參數,後面會影響到咱們分析代碼分支走向,這裏傳進來的是【false】,表示不啓動【子Zygote】。【startViaZygote】方法實現以下:

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,
                                                  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);
    ...
    
    synchronized(mLock) {
        return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
    }
}
複製代碼

這個方法主要作了兩件事:

1.封裝參數。這些參數最終會發給【zygote】,【zygote】就是根據這些參數來建立進程的;

2.調用zygoteSendArgsAndGetResult方法將參數發送數據;具體實現以下:

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

            for (int i = 0; i < sz; i++) {
                String arg = args.get(i);
                writer.write(arg);
                writer.newLine();
            }
            ...
            
        } catch (IOException ex) {
            zygoteState.close();
            throw new ZygoteStartFailedEx(ex);
        }
}
複製代碼

在這個方法中,咱們看到了【write】的操做,開始正式往【zygote】這個socket發具體的CMD指令了,這個操做主要事由【zygoteState】中的【writer】方法實現,而【zygoteState】做爲參數被傳進來:

zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
複製代碼

【zygoteState】是由【openZygoteSocketIfNeeded】建立返回的:

private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {

    ...
    try {
        primaryZygoteState = ZygoteState.connect(mSocket);
    } catch (IOException ioe) {
        throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);
    }
    
    return primaryZygoteState;
    ...
}
複製代碼

這裏調用了【ZygoteState】的connect方法,建立了socket鏈接,傳入的mSocket參數就是咱們上文提到的利用名爲【zygote】的字符串構建的。

這裏完成connect,而後再調用write進行發送指令。到了這裏,【AMS】發起進程建立的請求流程就分析完成,下一步看看【Zygote】是如何接收指令並解析command完成進程建立的。

Zygote端如何響應進程建立請求

就如開頭所說的,【zygote】在fork出【system_server】後,就留下了一個供外界聯繫本身的入口,那就是socket。因此要想響應外界的進程建立需求, 第一步就是要建立socket節點,而後再等待請求,而後解析指令參數,從而建立進程。看看流程圖就明白了:

image

讓咱們從socket建立提及吧,這得回到【ZygoteInit】的main方法中,【zygote】真正啓動的起點:

frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
public static void main(String argv[]) {


    ...
    
    zygoteServer.registerServerSocketFromEnv(socketName);
    
    ...
    
    caller = zygoteServer.runSelectLoop(abiList);
    if (caller != null) {
        caller.run();
    }
    
    ...
}
複製代碼

socket的建立經過【zygoteserver】的【registerServerSocketFromEnv】方法實現,這裏傳參【socketName】,咱們看看它的具體值:

String socketName = "zygote";
複製代碼

這裏看來就是要建立一個名爲【zygote】的socket了。咱們看看它的實現:

frameworks/base/core/java/com/android/internal/os/ZygoteServer.java
void registerServerSocketFromEnv(String socketName) {
    if (mServerSocket == 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 {
            FileDescriptor fd = new FileDescriptor();
            fd.setInt$(fileDesc);
            mServerSocket = new LocalServerSocket(fd);
            mCloseSocketFd = true;
        } catch (IOException ex) {
            throw new RuntimeException(
                    "Error binding to local socket '" + fileDesc + "'", ex);
        }
    }
}
複製代碼

果真在這邊建立了socket。接下來就是監聽這個socket,響應各類請求便可。咱們看看【runSelectLoop】的實現:

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

Runnable runSelectLoop(String abiList) {

    fds.add(mServerSocket.getFileDescriptor());
    ...
    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);
                    final Runnable command = connection.processOneCommand(this);
                    ...
                catch (Exception e) {
                    ...
                } finally {
                
                    ...
                }
            }
        }
    }

}
複製代碼

這裏看到該方法中將以前建立的socket對應文件描述符添加進來,而後調用系統的poll方法進行監聽。這裏poll方法的超時時間設置爲-1,即若是無相關指令過來,就會一直阻塞。當有指令過來的時候,首先經過i的值來進行判斷,若是是0,則表示此時還未贊成客戶端的鏈接請求,須要調用【acceptCommandPeer】方法贊成這次請求,並將該【connection】對應的文件描述符添加到監聽的集合中,下次發了數據過來,就能夠被監聽到,而後調用【processOneCommand】方法進行處理:

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

Runnable processOneCommand(ZygoteServer zygoteServer) {

    ...
    try {
        //讀取AMS發過來的參數
        args = readArgumentList();
        descriptors = mSocket.getAncillaryFileDescriptors();
    } catch (IOException ex) {
        throw new IllegalStateException("IOException on command socket", ex);
    }
    
    ...
    
    //開始解析參數,並保存到parsedArgs中
    parsedArgs = new Arguments(args);
    
    ...
    
    //根據解析出來的參數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) {
            // in child
            zygoteServer.setForkChild();

            zygoteServer.closeServerSocket();
            IoUtils.closeQuietly(serverPipeFd);
            serverPipeFd = null;

            //處理建立出來的子進程
            return handleChildProc(parsedArgs, descriptors, childPipeFd,
                    parsedArgs.startChildZygote);
        } else {
            // In the parent. A pid < 0 indicates a failure and will be handled in
            // handleParentProc.
            IoUtils.closeQuietly(childPipeFd);
            childPipeFd = null;
            handleParentProc(pid, descriptors, serverPipeFd);
            return null;
        }
    } finally {
        IoUtils.closeQuietly(childPipeFd);
        IoUtils.closeQuietly(serverPipeFd);
    }
}
複製代碼

該方法中主要作了四件事:

1.調用【readArgumentList】方法獲取客戶端發過來的數據;

2.解析參數,並保存到parsedArgs中;

3.根據解析出來的參數fork進程;

4.處理建立出來的子進程;

咱們進入到【handleChildProc】方法中,看看是如何實現的:

private Runnable handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors,
        FileDescriptor pipeFd, boolean isZygote) {
        
    ...
    closeSocket(); //子進程因爲也複製了zygote的socket資源,這裏須要進行關閉,子進程並不須要
    ...
     if (!isZygote) {
            return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs,
                    null /* classLoader */);
    } else {
        return ZygoteInit.childZygoteInit(parsedArgs.targetSdkVersion,
                parsedArgs.remainingArgs, null /* classLoader */);
    }
    ...
}
複製代碼

這裏有個isZygote變量,決定代碼走向。這個變量是從【AMS】中一路傳遞下來的,在上一個章節中有提到,傳遞下來的值是false,因此這裏咱們進入到【zygoteInit】中進行分析:

frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
public static final Runnable zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) {
    if (RuntimeInit.DEBUG) {
        Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from zygote");
    }

    ...
    return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
}
複製代碼

調用了【RuntimeInit】類的【applicationInit】方法:

protected static Runnable applicationInit(int targetSdkVersion, String[] argv,
        ClassLoader classLoader) {
    
    ...
    final Arguments args = new Arguments(argv);

    // The end of of the RuntimeInit event (see #zygoteInit).
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

    // Remaining arguments are passed to the start class's static main
    return findStaticMain(args.startClass, args.startArgs, classLoader);
}
複製代碼

很明顯,調用【findStaticMain】獲取fork出來的main方法,看看具體實現:

frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
protected static Runnable findStaticMain(String className, String[] argv,
        ClassLoader classLoader) {

     Class<?> cl;

    try {
        cl = Class.forName(className, true, classLoader);
    } catch (ClassNotFoundException ex) {
        throw new RuntimeException(
                "Missing class when invoking static main " + className,
                ex);
    }

    Method m;
    try {
        m = cl.getMethod("main", new Class[] { String[].class });
    } catch (NoSuchMethodException ex) {
        throw new RuntimeException(
                "Missing static main on " + className, ex);
    } catch (SecurityException ex) {
        throw new RuntimeException(
                "Problem getting static main on " + className, ex);
    }

    int modifiers = m.getModifiers();
    if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
        throw new RuntimeException(
                "Main method is not public and static on " + className);
    }
    
    return new MethodAndArgsCaller(m, argv);
}
複製代碼

經過反射獲取到對應進程的main方法,而後再將main方法看成參數傳給的【MethodAndArgsCaller】的構造函數:

static class MethodAndArgsCaller implements Runnable {
    /** method to call */
    private final Method mMethod;

    /** argument array */
    private final String[] mArgs;

    public MethodAndArgsCaller(Method method, String[] args) {
        mMethod = method;
        mArgs = args;
    }

    public void run() {
        try {
            mMethod.invoke(null, new Object[] { mArgs });
        } catch (IllegalAccessException ex) {
            throw new RuntimeException(ex);
        } catch (InvocationTargetException ex) {
            Throwable cause = ex.getCause();
            if (cause instanceof RuntimeException) {
                throw (RuntimeException) cause;
            } else if (cause instanceof Error) {
                throw (Error) cause;
            }
            throw new RuntimeException(ex);
        }
    }
}
複製代碼

這裏的run主要經過【invoke】方法調用進程的main方法。

【MethodAndArgsCaller】方法最終返回一個【Runnable】對象,該對象會被一路返回,最終返回到【ZygoteInit】,由caller拿到:

caller = zygoteServer.runSelectLoop(abiList);
if (caller != null) {
    caller.run();
}
複製代碼

而後調用【run】方法啓動進程。

結語

到了這裏,進程正式啓動,開始了它的一輩子。只要不作什麼犯法的事,就不會被殺。固然,關鍵是不要做(過於佔用內存和CPU資源),否則也會被系統殺死。

寫得好累,終於擼完。此時此刻想來瓶快樂肥皂水!!!

最後

我在微信公衆號也有寫文章,更新比較及時,有興趣者能夠掃描以下二維碼,或者微信搜索【Android系統實戰開發】,關注有驚喜哦!

相關文章
相關標籤/搜索