從源碼角度看AMS.startProcessLocked

簡介

(本文原地址在個人博客CheapTalks, 歡迎你們來看看~)java

注:本篇文章的全部源碼與註釋均可以在YogiAi/Process.start中找到,只想閱讀代碼的同窗能夠直奔主題。android

衆所周知,Android 系統是基於 Linux 內核的移動操做系統。而 Linux 又是經過 fork 來複制進程,複製的時候只是建立惟一識別符等輕量操做,真正對於資源的使用是藉助了寫時複製的機制(copy-on-write)。進程與線程的概念在 Linux 的世界中只有資源擁有的差異,本質上它們的內核實現都使用了 task_struct 這同一個結構體,都擁有着各自的 PID, PPID。c++

在 Android 自成的上層 framework 世界中,進程的概念被層層的封裝後已經很模糊了,基本開發者完成開發過程只需熟悉Activity, BroadcastReceiver, Service等四大組件的做用與使用場景,再加上網絡訪問、數據存儲、UI 繪製等等組合而成的業務邏輯便可完成一個很不錯的應用。git

然而研究 Android 的進程啓動時機與實現原理對於進階學習仍是大有裨益的,不只可以學到進程線程的內功知識,也可以學到設計大師的封裝奧妙。Android 應用進程的建立是經過 fork Zygote 進程來實現的,因此全部的應用進程的 PPID 都是 Zygote 的 PID。複製 Zygote 的實例後會獲得一個虛擬機實例,除此以外,新建的進程還會獲取到一個消息循環、 Binder 的進程通訊池以及一個 Binder 主線程。github

在這一篇文章中,我將詳細解析Android 進程的建立過程。數組

system_server 端

AMS.startProcessLocked

ActivityManagerService 運行在 system_server 進程,爲應用提供各類服務。系統可能由於發送廣播,啓動服務,運行 Activity 等緣由啓動一個新的進程。這時候會 binder call 到 AMS,調用 startProcessLocked 方法進行處理。網絡

final ProcessRecord startProcessLocked(String processName, ApplicationInfo info, boolean knownToBeDead, int intentFlags, String hostingType, ComponentName hostingName, boolean allowWhileBooting) {
...
        if (app == null) {
            app = newProcessRecordLocked(null, info, processName);
            mProcessNames.put(processName, info.uid, app);
        } else {
            // If this is a new package in the process, add the package to the list
            app.addPackage(info.packageName);
        }
...
        startProcessLocked(app, hostingType, hostingNameStr);
        return (app.pid != 0) ? app : null;
    }複製代碼
private final void startProcessLocked(ProcessRecord app, String hostingType, String hostingNameStr) {
...
        try {
            // 得到建立的應用程序進程的用戶 ID,用戶組 ID
            int uid = app.info.uid;
            int[] gids = null;
            try {
                gids = mContext.getPackageManager().getPackageGids(
                        app.info.packageName);
            } catch (PackageManager.NameNotFoundException e) {
                Slog.w(TAG, "Unable to retrieve gids", e);
            }
...
            // 建立進程,並制定這個進程的路口時 ActivityThread 的靜態方法 main
            int pid = Process.start("android.app.ActivityThread",
                    mSimpleProcessManagement ? app.processName : null, uid, uid,
                    gids, debugFlags, null);
...
    }複製代碼

Process.start

進程的啓動入口在 Process.start 方法,start 方法只作了初始化參數的工做,真正的複製進程工做是在 zygote 進行完成的,system_server 與 zygote 進程的通訊使用的 socket。system_server 會將參數信息寫入到 socket 中,而後阻塞等待 zygote 的迴應。架構

public static final int start(final String processClass, final String niceName, int uid, int gid, int[] gids, int debugFlags, String[] zygoteArgs) {
        // 是否支持 Binder 進程間通訊的機制
        if (supportsProcesses()) {
            try {
                // 若是支持, 就請求 Zygote 來建立一個應用程序進程
                return startViaZygote(processClass, niceName, uid, gid, gids,
                        debugFlags, zygoteArgs);
            } catch (ZygoteStartFailedEx ex) {
                Log.e(LOG_TAG,
                        "Starting VM process through Zygote failed");
                throw new RuntimeException(
                        "Starting VM process through Zygote failed", ex);
            }
        } else {
            // Running in single-process mode
            // 若是不支持, 就使用一個線程來模擬進程
            Runnable runnable = new Runnable() {
                        public void run() {
                            Process.invokeStaticMain(processClass);
                        }
            };

            // Thread constructors must not be called with null names (see spec). 
            if (niceName != null) {
                new Thread(runnable, niceName).start();
            } else {
                new Thread(runnable).start();
            }

            return 0;
        }
    }複製代碼
private static int startViaZygote(final String processClass, final String niceName, final int uid, final int gid, final int[] gids, int debugFlags, String[] extraArgs) throws ZygoteStartFailedEx {
        int pid;

        synchronized(Process.class) {
            // 初始化進程的啓動參數列表
            ArrayList<String> argsForZygote = new ArrayList<String>();
...

            // 初始化完畢
            pid = zygoteSendArgsAndGetPid(argsForZygote);
        }     
...
        return pid;
    }複製代碼
private static int zygoteSendArgsAndGetPid(ArrayList<String> args) throws ZygoteStartFailedEx {
        int pid;

        // 建立一個鏈接到 Zygote 的 LocalSocket 對象
        openZygoteSocketIfNeeded();

        try {
            // 將要建立的應用程序的進程啓動參數傳到 LocalSocket 對象中
            sZygoteWriter.write(Integer.toString(args.size()));
            sZygoteWriter.newLine();

            int sz = args.size();
            for (int i = 0; i < sz; i++) {
                String arg = args.get(i);
                if (arg.indexOf('\n') >= 0) {
                    throw new ZygoteStartFailedEx(
                            "embedded newlines not allowed");
                }
                sZygoteWriter.write(arg);
                sZygoteWriter.newLine();
            }

            sZygoteWriter.flush();

            // Should there be a timeout on this?
            // 經過 Socket 讀取 Zygote 建立成功的進程 PID
            // Socket 對端的請求在 ZygoteInit.runSelectLoopMode中進行處理
            // 讀取成功以後會對 PID 進行檢查,無異常的話就會推出
            pid = sZygoteInputStream.readInt();

            if (pid < 0) {
                throw new ZygoteStartFailedEx("fork() failed");
            }
        } catch (IOException ex) {
...
        }

        return pid;
    }複製代碼

zygote 端

ZygoteInit.main

zygote 進程的 Socket服務端是在此處進行建立初始化的,當接受到 socket 客戶端的請求時會進行處理。本質上是啓動了一個無限循環來處理客戶端的請求。這裏 system_server 與 zygote 是典型的 C/S 架構。app

public static void main(String argv[]) {
        try {
            VMRuntime.getRuntime().setMinimumHeapSize(5 * 1024 * 1024);

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

            // 在 Zygote 服務端註冊一個 Socket Server, 用來建立新進程
            registerZygoteSocket();
...

            if (ZYGOTE_FORK_MODE) {
                runForkMode();
            } else {
                // 開始處理進程建立的 Socket 請求
                runSelectLoopMode();
            }

            closeServerSocket();
        } catch (MethodAndArgsCaller caller) {
            // ActivityThread 的靜態方法在這被回調執行
            // 這裏間接調用方法,巧妙的利用了異常處理機制來清理前面的調用棧
            caller.run();
        } catch (RuntimeException ex) {
            Log.e(TAG, "Zygote died with exception", ex);
            closeServerSocket();
            throw ex;
        }
    }複製代碼
private static void runSelectLoopMode() throws MethodAndArgsCaller {
        ArrayList<FileDescriptor> fds = new ArrayList();
        ArrayList<ZygoteConnection> peers = new ArrayList();
        FileDescriptor[] fdArray = new FileDescriptor[4];

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

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

            if (index < 0) {
                throw new RuntimeException("Error in select()");
            } else if (index == 0) {
                // 新的進程建立請求
                ZygoteConnection newPeer = acceptCommandPeer();
                peers.add(newPeer);
                fds.add(newPeer.getFileDesciptor());
            } else {
                boolean done;
                // 處理這個進程請求
                done = peers.get(index).runOnce();

                if (done) {
                    peers.remove(index);
                    fds.remove(index);
                }
            }
        }
    }複製代碼

ZygoteConnection.runOnce

在 zygote 進程主要執行這三個操做:less

  1. 調用 Zygote.forkAndSpecialize 進行進程複製操做
  2. 調用 handleChildProc 處理新建進程資源初始化,如建立 Binder 線程池,啓動一個主線程消息隊列
  3. 調用 handleParentProc 將新建進程的 PID 返回給 system_server,表示建立結果
boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
        String args[];
        Arguments parsedArgs = null;
        FileDescriptor[] descriptors;

        try {
            // 讀取啓動參數
            args = readArgumentList();
            descriptors = mSocket.getAncillaryFileDescriptors();
        } catch (IOException ex) {
            Log.w(TAG, "IOException on command socket " + ex.getMessage());
            closeSocket();
            return true;
        }
...

        int pid;

        try {
            // 將 String 數組封裝成 Arguments
            parsedArgs = new Arguments(args);

            applyUidSecurityPolicy(parsedArgs, peer);
            applyDebuggerSecurityPolicy(parsedArgs);
            applyRlimitSecurityPolicy(parsedArgs, peer);
            applyCapabilitiesSecurityPolicy(parsedArgs, peer);

            int[][] rlimits = null;

            if (parsedArgs.rlimits != null) {
                rlimits = parsedArgs.rlimits.toArray(intArray2d);
            }

            // fork 操做
            // 將會有兩個進程從這裏返回
            // PID=0意味着是子進程
            pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid,
                    parsedArgs.gids, parsedArgs.debugFlags, rlimits);
        } catch (IllegalArgumentException ex) {
            logAndPrintError (newStderr, "Invalid zygote arguments", ex);
            pid = -1;
        } catch (ZygoteSecurityException ex) {
            logAndPrintError(newStderr,
                    "Zygote security policy prevents request: ", ex);
            pid = -1;
        }

        if (pid == 0) {
            // in child
            // 建立出的新進程
            handleChildProc(parsedArgs, descriptors, newStderr);
            // should never happen
            return true;
        } else { /* pid != 0 */
            // in parent...pid of < 0 means failure
            // 父進程將在這裏進行處理
            return handleParentProc(pid, descriptors, parsedArgs);
        }
    }複製代碼
private void handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors, PrintStream newStderr) throws ZygoteInit.MethodAndArgsCaller {
...
        if (parsedArgs.runtimeInit) {
            // 在新建立的應用程序進程中初始化運行時庫,建立一個 Binder 線程池
            RuntimeInit.zygoteInit(parsedArgs.remainingArgs);
        } else {
            ClassLoader cloader;

               // 獲取 classloader
            if (parsedArgs.classpath != null) {
                cloader
                    = new PathClassLoader(parsedArgs.classpath,
                    ClassLoader.getSystemClassLoader());
            } else {
                cloader = ClassLoader.getSystemClassLoader();
            }

              // 讀取 ActivityThread 類名
            String className;
            try {
                className = parsedArgs.remainingArgs[0];
            } catch (ArrayIndexOutOfBoundsException ex) {
                logAndPrintError (newStderr,
                        "Missing required class name argument", null);
                return;
            }
            String[] mainArgs
                    = new String[parsedArgs.remainingArgs.length - 1];

            System.arraycopy(parsedArgs.remainingArgs, 1,
                    mainArgs, 0, mainArgs.length);

            try {
                // 觸發 ActivityThread.main 方法
                ZygoteInit.invokeStaticMain(cloader, className, mainArgs);
            } catch (RuntimeException ex) {
                logAndPrintError (newStderr, "Error starting. ", ex);
            }
        }
    }複製代碼
private boolean handleParentProc(int pid, FileDescriptor[] descriptors, Arguments parsedArgs) {
...
        try {
            // 在這裏經過 Socket 通知對端進程已經建立成功,並返回 PID
            mSocketOutStream.writeInt(pid);
        } catch (IOException ex) {
            Log.e(TAG, "Error reading from command socket", ex);
            return true;
        }
...
        return false;
    }複製代碼

Zygote.forkAndSpecialize

進行進程建立的工做也是主要作了三件事:

  1. 調用 ZygoteHooks.preFork 中止上次建立進程的 daemon 線程
  2. 調用 nativeForkAndSpecialize 在 c++層建立新進程
  3. 調用 ZygoteHooks.postForkCoomon 在父進程、子進程中啓動 daemon 線程
public static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose, int[] fdsToIgnore, String instructionSet, String appDataDir) {
        VM_HOOKS.preFork();
        // Resets nice priority for zygote process.
        resetNicePriority();
        // 進入內核層進行進程的 fork 操做
        int pid = nativeForkAndSpecialize(
                  uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
                  fdsToIgnore, instructionSet, appDataDir);
...
        VM_HOOKS.postForkCommon();
        return pid;
    }複製代碼

native層

Zygote.cpp

這裏先看進程的複製

static jint com_android_internal_os_Zygote_nativeForkAndSpecialize( JNIEnv* env, jclass, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring se_name, jintArray fdsToClose, jintArray fdsToIgnore, jstring instructionSet, jstring appDataDir) {
...
    return ForkAndSpecializeCommon(env, uid, gid, gids, runtime_flags,
            rlimits, capabilities, capabilities, mount_external, se_info,
            se_name, false, fdsToClose, fdsToIgnore, instructionSet, appDataDir);
}複製代碼
// Utility routine to fork zygote and specialize the child process.
static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGids, jint runtime_flags, jobjectArray javaRlimits, jlong permittedCapabilities, jlong effectiveCapabilities, jint mount_external, jstring java_se_info, jstring java_se_name, bool is_system_server, jintArray fdsToClose, jintArray fdsToIgnore, jstring instructionSet, jstring dataDir) {
  SetSigChldHandler();

  sigset_t sigchld;
  sigemptyset(&sigchld);
  sigaddset(&sigchld, SIGCHLD);
...
  // 進行 fork
  pid_t pid = fork();

  if (pid == 0) {
    // 子進程操做
    PreApplicationInit();

    // Clean up any descriptors which must be closed immediately
    DetachDescriptors(env, fdsToClose);
...

    // Keep capabilities across UID change, unless we're staying root.
    if (uid != 0) {
      EnableKeepCapabilities(env);
    }

    SetInheritable(env, permittedCapabilities);
    DropCapabilitiesBoundingSet(env);
...
    if (!is_system_server) {
        // 若是不是 system_server 進程,須要建立進程組
        int rc = createProcessGroup(uid, getpid());
        if (rc != 0) {
            if (rc == -EROFS) {
                ALOGW("createProcessGroup failed, kernel missing CONFIG_CGROUP_CPUACCT?");
            } else {
                ALOGE("createProcessGroup(%d, %d) failed: %s", uid, pid, strerror(-rc));
            }
        }
    }

    SetGids(env, javaGids);

    SetRLimits(env, javaRlimits);
...

    SetCapabilities(env, permittedCapabilities, effectiveCapabilities, permittedCapabilities);

    // 設置進程調度策略
    SetSchedulerPolicy(env);
  } else if (pid > 0) {
    // Zygote 進程將會執行這裏
...
  }
  return pid;
}複製代碼

fork.cpp

調用 clone 方法進行進程的複製,複製完成後父進程與子進程會調用各自的回調方法

int fork() {
  __bionic_atfork_run_prepare();

  pthread_internal_t* self = __get_thread();

  int result = clone(nullptr,
                     nullptr,
                     (CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | SIGCHLD),
                     nullptr,
                     nullptr,
                     nullptr,
                     &(self->tid));
  if (result == 0) {
    // Update the cached pid, since clone() will not set it directly (as
    // self->tid is updated by the kernel).
    self->set_cached_pid(gettid());
    // 調用子進程的回調,具體代碼參考 pthread.atfork.cpp
    __bionic_atfork_run_child();
  } else {
    __bionic_atfork_run_parent();
  }
  return result;
}複製代碼

pthread_atfork.cpp

template<typename F>
  void walk_forward(F f) {
    for (atfork_t* it = first_; it != nullptr; it = it->next) {
      f(it);
    }
  }

void __bionic_atfork_run_prepare() {
  // We lock the atfork list here, unlock it in the parent, and reset it in the child.
  // This ensures that nobody can modify the handler array between the calls
  // to the prepare and parent/child handlers.
  pthread_mutex_lock(&g_atfork_list_mutex);

  // Call pthread_atfork() prepare handlers. POSIX states that the prepare
  // handlers should be called in the reverse order of the parent/child
  // handlers, so we iterate backwards.
  g_atfork_list.walk_backwards([](atfork_t* it) {
    if (it->prepare != nullptr) {
      it->prepare();
    }
  });
}

void __bionic_atfork_run_child() {
  g_atfork_list_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;

  pthread_mutex_lock(&g_atfork_list_mutex);
  g_atfork_list.walk_forward([](atfork_t* it) {
    if (it->child != nullptr) {
      it->child();
    }
  });
  pthread_mutex_unlock(&g_atfork_list_mutex);
}複製代碼

ZygoteHooks.cc

再看在執行進程 fork 先後的虛擬機操做

static jlong ZygoteHooks_nativePreFork(JNIEnv* env, jclass) {
  Runtime* runtime = Runtime::Current();
  CHECK(runtime->IsZygote()) << "runtime instance not started with -Xzygote";

  runtime->PreZygoteFork();

  // Grab thread before fork potentially makes Thread::pthread_key_self_ unusable.
  return reinterpret_cast<jlong>(ThreadForEnv(env));
}複製代碼
static void ZygoteHooks_nativePostForkChild(JNIEnv* env, jclass, jlong token, jint debug_flags, jstring instruction_set) {
  Thread* thread = reinterpret_cast<Thread*>(token);
  // Our system thread ID, etc, has changed so reset Thread state.
  thread->InitAfterFork();
  EnableDebugFeatures(debug_flags);

  if (instruction_set != nullptr) {
    ScopedUtfChars isa_string(env, instruction_set);
    InstructionSet isa = GetInstructionSetFromString(isa_string.c_str());
    Runtime::NativeBridgeAction action = Runtime::NativeBridgeAction::kUnload;
    if (isa != kNone && isa != kRuntimeISA) {
      action = Runtime::NativeBridgeAction::kInitialize;
    }
    Runtime::Current()->DidForkFromZygote(env, action, isa_string.c_str());
  } else {
    Runtime::Current()->DidForkFromZygote(env, Runtime::NativeBridgeAction::kUnload, nullptr);
  }
}複製代碼

runtime.cc

自此,新進程建立成功,相關的環境也已經初始化完畢

void Runtime::PreZygoteFork() {
  heap_->PreZygoteFork();
}

void Runtime::DidForkFromZygote(JNIEnv* env, NativeBridgeAction action, const char* isa) {
  is_zygote_ = false;

  if (is_native_bridge_loaded_) {
    switch (action) {
      case NativeBridgeAction::kUnload:
        UnloadNativeBridge();
        is_native_bridge_loaded_ = false;
        break;

      case NativeBridgeAction::kInitialize:
        // 跨平臺橋連庫
        InitializeNativeBridge(env, isa);
        break;
    }
  }

  // Create the thread pools.
  // 建立 java 堆處理線程池
  heap_->CreateThreadPool();
  if (jit_options_.get() != nullptr && jit_.get() == nullptr) {
    // Create the JIT if the flag is set and we haven't already create it (happens for run-tests).
    // 建立 JIT
    CreateJit();
    jit_->CreateInstrumentationCache(jit_options_->GetCompileThreshold());
    jit_->CreateThreadPool();
  }

  // 設置信號處理函數
  StartSignalCatcher();

  // Start the JDWP thread. If the command-line debugger flags specified "suspend=y",
  // this will pause the runtime, so we probably want this to come last.
  // 啓動JDWP線程,當命令debuger的flags指定"suspend=y"時,則暫停runtime
  Dbg::StartJdwp();
}複製代碼

ActivityThread.main

回到 java framework 層,新進程啓動成功後會運行 ActivityThread 的 main, 開啓一個主線程的消息隊列,等待與 system_server 進行交互。

public static final void main(String[] args) {
        SamplingProfilerIntegration.start();

        Process.setArgV0("<pre-initialized>");

        // 建立主線程消息循環
        // 每個應用程序啓動完成以後都會自動的進行這個消息循環
        Looper.prepareMainLooper();
        if (sMainThreadHandler == null) {
            sMainThreadHandler = new Handler();
        }

        // 建立 ActivityThread 實例
        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        Looper.loop();

        if (Process.supportsProcesses()) {
            throw new RuntimeException("Main thread loop unexpectedly exited");
        }

        thread.detach();
        String name = (thread.mInitialApplication != null)
            ? thread.mInitialApplication.getPackageName()
            : "<unknown>";
        Slog.i(TAG, "Main thread of " + name + " is now exiting");
    }複製代碼

總結

  1. Android 應用的進程建立入口點在 AMS.startProcessLocked, 經過調用 Process.start 來發出一個 socket request 來請求 zygote 進程進行 fork, 建立一個新的進程。
  2. 本質上覆制進程只須要調用 fork 方法便可,可是 Android 對於新進程的操做有着額外的封裝。一個新進程的誕生作了如下三點工做:
    • 寫時複製了 zygote 進程
    • 開啓了一個 Binder 進程池方便進程 IPC 操做
    • 開啓了一個主線程消息循環
  3. 進程的建立工做是持有者 AMS 的鎖進行的,若是 Zygote 由於 CPU 負載太高或者內存缺少等等緣由建立進程的速度變慢,使得 system_server 其它 Binder 線程阻塞,那麼頗有可能會形成第三方應用間接的耗時和ANR
相關文章
相關標籤/搜索