天天看點

Android Framework系列(從系統開機到應用啟動篇)

前言:Android手機從系統開機到應用啟動,裡面程涉及到的Framework層、核心層知識還是挺多的,作為一名開發者,熟悉常見的通信方式(Socket、Binder、Handler等)、了解Android系統的整體架構勢在必行。本篇文章帶領大家一起來領略Android系統從開機到應用啟動的前世今生。

整體大綱:

Android Framework系列(從系統開機到應用啟動篇)

一、Launcher程序的啟動過程

主要分為以下幾個步驟:

  1. Linux核心啟動
  2. init程序啟動
  3. init程序fork出Zygote程序
  4. Zygote程序fork出SystemServer程序
  5. SystemServer程序啟動各項服務(PMS、AMS等)
  6. AMS服務啟動Launcher桌面
    Android Framework系列(從系統開機到應用啟動篇)

1. Linux核心啟動

從boot loader跳轉到linux kernel層,執行第一條指令。

2. init程序啟動

init程序是Linux系統中使用者空間的第一個程序,程序号固定為1。Kernel啟動後,在使用者空間啟動init程序,并調用init中的main()方法執行init程序的職責。

3. Zytote程序的啟動過程

Android Framework系列(從系統開機到應用啟動篇)

總結:init程序讀取配置檔案init.rc後,fork出Zygote程序,通過execve函數執行Zygote的執行程式app_process,進入ZygoteInit類的main函數。

  1. init程序讀取init.rc檔案
//system/core/rootdir/init.rc
  import /init.${ro.zygote}.rc
           

${ro.zygote}的取值有4種,在init.rc的同級目錄/system/core/rootdir下,可以看到4個Zygote相關的配置檔案,表示系統所支援程式的bit位數,

  • init.zygote32.rc,Zygote程序的執行程式路徑為/system/bin/app_process
  • init.zygote64.rc,Zygote程序的執行程式路徑為/system/bin/app_process64
  • init.zygote32_64.rc,會啟動兩個Zygote程序,有兩個執行程式,32為主模式
  • init.zygote64_32.rc,會啟動兩個Zygote程序,有兩個執行程式,64為主模式
  1. init.zygote32.rc配置檔案:
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    class main
    priority -20
    user root
    group root readproc reserved_disk
    socket zygote stream 660 root system
    ......
           

第一行中:

  • service表示Zygote程序以服務的形式來啟動
  • zygote則是程序的名字,/system/bin/app_process是執行程式的路徑,
  • start-system-server表示在Zygote程序啟動後需要啟動SystemServer程序。

第六行中:

  • Zygote程序是使用socket來進行跨程序通信的,是以會建立一個名為zygote的socket,
  • 660表示通路權限rw-rw----,表示檔案擁有者和同一群組使用者具有讀寫權限。
  1. 通過fork出zygote程序,再通過execve方法來執行zygote的執行程式app_proces.main(),然後AndroidRuntime.start() ->ZygoteInit.main() 至此Zygote就正式啟動了:
//system/core/init/service.cpp
bool Service::Start() {
    //fork出子程序
    pid = fork();
    if (pid == 0) {//子程序會傳回0,父程序會傳回子程序的pid
        //strs[0]是執行程式的路徑,即execve會運作app_process
        if (execve(strs[0], (char**) &strs[0], (char**) ENV) < 0) {
        }
    }
}
           
//frameworks/base/cmds/app_process/app_main.cpp
int main(int argc, char* const argv[]){
    if (zygote) {
        //啟動Zygote,進入ZygoteInit.main函數,執行AndroidRuntime.start()方法
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
    }
}
           
//frameworks/base/core/jni/AndroidRuntime.cpp
void AndroidRuntime::start(...){
    //1. 啟動java虛拟機
    if (startVm(&mJavaVM, &env, zygote) != 0) {
        return;
    }
    //2. 為java虛拟機注冊JNI方法
    if (startReg(env) < 0) {
        return;
    }
    //根據傳入的參數找到ZygoteInit類和他的main函數
    //3. 通過JNI調用ZygoteInit的main函數
    env->CallStaticVoidMethod(startClass, startMeth, strArray);
}
           
//ZygoteInit.java
public static void main(String argv[]) {
    //是否要建立SystemServer
    boolean startSystemServer = false;
    //預設的socket名字
    String socketName = "zygote";
    //是否要延遲資源的預加載
    boolean enableLazyPreload = false;

    for (int i = 1; i < argv.length; i++) {
        if ("start-system-server".equals(argv[i])) {
            //在init.rc檔案中,有--start-system-server參數,表示要建立SystemServer
            startSystemServer = true;
        } else if ("--enable-lazy-preload".equals(argv[i])) {
            //init.rc沒有這個參數,資源的預加載不會被延遲
            enableLazyPreload = true;
        } else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
            //init.rc可以通過--socket-name=指定socket名字來覆寫預設值
            socketName = argv[i].substring(SOCKET_NAME_ARG.length());
        }
    }

    //1. 建立服務端socket,名字為socketName即zygote
    zygoteServer.registerServerSocket(socketName);

    if (!enableLazyPreload) {
        //2. 沒有被延遲,就預加載資源
        preload(bootTimingsTraceLog);
    }

    if (startSystemServer) {
        //3. fork并啟動SystemServer程序
        startSystemServer(abiList, socketName, zygoteServer);
    }

    //4. 等待AMS請求(AMS會通過socket請求Zygote來建立應用程式程序)
    zygoteServer.runSelectLoop(abiList);
}
           

4. SystemServer的啟動過程

Android Framework系列(從系統開機到應用啟動篇)

總結:SystemServer程序被建立後,主要做了3件事情:啟動binder線程池、建立SystemServiceManager(SSM)、用SSM啟動各種服務。

  1. 這裡啟動了binder線程池,SystemServer程序就可以用binder機制來跨程序通信了(Zygote程序是用socket來通信的),接着進入了SystemServer的main函數:
//ZygoteInit.java
private static boolean startSystemServer(...){
    String args[] = {
        //...
        //啟動的類名:
        "com.android.server.SystemServer",
    };
    //fork程序,由native層實作
    pid = Zygote.forkSystemServer();
    //處理SystemServer程序
    handleSystemServerProcess(parsedArgs);
}

private static void handleSystemServerProcess(...){
    ZygoteInit.zygoteInit(...);
}

public static final void zygoteInit(...){
    //啟動binder線程池
    ZygoteInit.nativeZygoteInit();
    //内部經過層層調用,找到"com.android.server.SystemServer"類和他的main函數,然後執行
    RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
}
           
//SystemServer.java
public static void main(String[] args) {
    new SystemServer().run();
}

private void run() {
    //建立looper
    Looper.prepareMainLooper();
    //加載動态庫libandroid_servers.so
    System.loadLibrary("android_servers");
    //建立系統上下文
    createSystemContext();

    //建立SSM,用于服務的建立、啟動和生命周期管理
    mSystemServiceManager = new SystemServiceManager(mSystemContext);
    
    //服務根據優先級被分成3批來啟動:
    //啟動引導服務,如AMS、PMS等
    startBootstrapServices();
    //啟動核心服務
    startCoreServices();
    //啟動其他服務
    startOtherServices();

    //開啟looper循環
    Looper.loop();
}
           
//SystemServer.java
private void startBootstrapServices() {
    //由SSM建立啟動
    mActivityManagerService = mSystemServiceManager.startService(
        ActivityManagerService.Lifecycle.class).getService();
    mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
}

private void startOtherServices() {
    //AMS準備就緒
    mActivityManagerService.systemReady(...);
}
           

5. Launcher的啟動過程

Android Framework系列(從系統開機到應用啟動篇)

總結:Launcher作為Android的桌面,用于管理應用圖示和桌面元件。SystemServer程序會啟動各種服務,其中PackageManagerService啟動後會将系統中的應用程式安裝完成,然後由AMS來啟動Launcher。

  1. SystemServer的startOtherServices()方法中啟動了AMS服務,然後調用了AMS的systemReady()方法,最後來到了startHomeActivityLocked(),啟動的Activity類是Launcher.java,剩下的流程就是加載已安裝的應用程式資訊。**
//SystemServer.java
private void startOtherServices() {
    //AMS準備就緒
    mActivityManagerService.systemReady(...);
}
           
//ActivityManagerService.java
public void systemReady(...) {
    //經過層層調用來到startHomeActivityLocked
    startHomeActivityLocked(...);
}
           
//ActivityManagerService.java
boolean startHomeActivityLocked(...) {
    //最終會啟動Launcher應用的Activity
    mActivityStarter.startHomeActivityLocked(...);
}
           

二、Launcher啟動應用App的過程

主要分為以下幾個步驟:

  • AMS發送socket請求
  • Zygote處理socket請求
  • Zygote程序fork出應用程序,應用程序繼承得到虛拟機執行個體
  • 應用程序啟動binder線程池、運作ActivityThread類的main函數、啟動Looper循環
    Android Framework系列(從系統開機到應用啟動篇)

1. AMS發送socket請求

Android應用程序的啟動是被動式的,在Launcher桌面點選圖示啟動一個應用的元件如Activity時,如果Activity所在的程序不存在,就會建立并啟動程序。點選App圖示後經過層層調用會來到ActivityStackSupervisor的startSpecificActivityLocked方法:

//ActivityStackSupervisor.java
final ActivityManagerService mService;

void startSpecificActivityLocked(...) {
    //查找Activity所在的程序,ProcessRecord是用來封裝程序資訊的資料結構
    ProcessRecord app = mService.getProcessRecordLocked(...);
	//如果程序已啟動,并且binder句柄IApplicationThread也拿到了,那就直接啟動Activity
    if (app != null && app.thread != null) {
        realStartActivityLocked(r, app, andResume, checkConfig);
        return;
    }
	//否則,讓AMS啟動程序
    mService.startProcessLocked(...);
}
           
  • app.thread并不是線程,而是一個binder句柄。應用程序使用AMS需要拿到AMS的句柄IActivityManager,而系統需要通知應用和管理應用的生命周期,是以也需要持有應用程序的binder句柄IApplicationThread。

    也就是說,他們互相持有彼此的binder句柄,來實作雙向通信:

Android Framework系列(從系統開機到應用啟動篇)

那IApplicationThread句柄是怎麼傳給AMS的呢?Zygote程序收到socket請求後會處理請求參數,執行ActivityThread的入口函數main:

//ActivityThread.java
public static void main(String[] args) {
    //建立主線程的looper
    Looper.prepareMainLooper();
    //ActivityThread并不是線程,隻是普通的java對象
    ActivityThread thread = new ActivityThread();
    //告訴AMS,應用已經啟動好了
    thread.attach(false);
	//運作looper,啟動消息循環
    Looper.loop();
}

private void attach(boolean system) {
    //擷取AMS的binder句柄IActivityManager
    final IActivityManager mgr = ActivityManager.getService();
    //告訴AMS應用程序已經啟動,并傳入應用程序自己的binder句柄IApplicationThread
    mgr.attachApplication(mAppThread);
}
           

是以對于AMS來說,AMS向Zygote發起啟動應用的socket請求,Zygote收到請求fork出程序,傳回程序的pid給AMS;應用程序啟動好後,執行入口main函數,通過attachApplication方法告訴AMS已經啟動,同時傳入應用程序的binder句柄IApplicationThread。完成這兩步,應用程序的啟動過程才算完成。

下面看AMS的startProcessLocked啟動應用程序時都做了些什麼?

//ActivityManagerService.java
final ProcessRecord startProcessLocked(...){
    ProcessRecord app = getProcessRecordLocked(processName, info.uid, keepIfLarge);
    //如果程序資訊不為空,并且已經拿到了Zygote程序傳回的應用程序pid
    //說明AMS已經請求過了,并且Zygote已經響應請求然後fork出程序了
    if (app != null && app.pid > 0) {
        //但是app.thread還是空,說明應用程序還沒來得及注冊自己的binder句柄給AMS
        //即此時程序正在啟動,那就直接傳回,避免重複建立
        if (app.thread == null) {
            return app;
        }
    }
    //調用重載方法
    startProcessLocked(...);
}
           

之是以要判斷app.thread,是為了避免當應用程序正在啟動的時候,假如又有另一個元件需要啟動,導緻重複拉起(建立)應用程序。

繼續看重載方法startProcessLocked,

//ActivityManagerService.java
private final void startProcessLocked(...){
    //應用程序的主線程的類名
    if (entryPoint == null) entryPoint = "android.app.ActivityThread";
    ProcessStartResult startResult = Process.start(entryPoint, ...);
}

//Process.java
public static final ProcessStartResult start(...){
    return zygoteProcess.start(...);
}
           

來到ZygoteProcess,

//ZygoteProcess.java
public final Process.ProcessStartResult start(...){
    return startViaZygote(...);
}

private Process.ProcessStartResult startViaZygote(...){
    ArrayList<String> argsForZygote = new ArrayList<String>();
    //...處理各種參數
    return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
}
           

其中:

  1. openZygoteSocketIfNeeded打開本地socket
  2. zygoteSendArgsAndGetResult發送請求參數,其中帶上了ActivityThread類名
  3. return傳回的資料結構ProcessStartResult中會有pid字段

整體梳理一下:

Android Framework系列(從系統開機到應用啟動篇)

2. Zygote處理socket請求

在ZygoteInit的main函數中,會建立服務端socket,

//ZygoteInit.java
public static void main(String argv[]) {
    //Server類,封裝了socket
    ZygoteServer zygoteServer = new ZygoteServer();
    //建立服務端socket,名字為socketName即zygote
    zygoteServer.registerServerSocket(socketName);
    //進入死循環,等待AMS發請求過來
    zygoteServer.runSelectLoop(abiList);
           
//ZygoteServer.java
void registerServerSocket(String socketName) {
    int fileDesc;
    //socket真正的名字被加了個字首,即 "ANDROID_SOCKET_" + "zygote"
    final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;

    String env = System.getenv(fullSocketName);
    fileDesc = Integer.parseInt(env);

    //建立檔案描述符fd
    FileDescriptor fd = new FileDescriptor();
    fd.setInt$(fileDesc);
    //建立LocalServerSocket對象
    mServerSocket = new LocalServerSocket(fd);
}

void runSelectLoop(String abiList){
    //進入死循環
    while (true) {
        for (int i = pollFds.length - 1; i >= 0; --i) {
            if (i == 0) {
                //...
            } else {
                //得到一個連接配接對象ZygoteConnection,調用他的runOnce
                boolean done = peers.get(i).runOnce(this);
            }
        }
    }
}
           
//ZygoteConnection.java
boolean runOnce(ZygoteServer zygoteServer){
    //讀取socket請求的參數清單
    String args[] = readArgumentList();
    //建立應用程序
    int pid = Zygote.forkAndSpecialize(...);
    if (pid == 0) {
        //如果是應用程序(Zygote fork出來的子程序),處理請求參數
        handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
        return true;
    } else {
        return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
    }
}
           

handleChildProc方法調用了ZygoteInit的zygoteInit方法,裡邊主要做了3件事:

  1. 啟動binder線程池(後面分析)
  2. 讀取請求參數拿到ActivityThread類并執行他的main函數,執行thread.attach告知AMS并回傳自己的binder句柄
  3. 執行Looper.loop()啟動消息循環(代碼前面有)

這樣應用程序就啟動起來了。梳理一下:

Android Framework系列(從系統開機到應用啟動篇)

3.啟動binder線程池

Zygote的跨程序通信沒有使用binder,而是socket,是以應用程序的binder機制不是繼承而來,而是程序建立後自己啟動的。前邊可知,Zygote收到socket請求後會得到一個ZygoteConnection,他的runOnce會調用handleChildProc,

//ZygoteConnection.java
private void handleChildProc(...){
    ZygoteInit.zygoteInit(...);
}

//ZygoteInit.java
public static final void zygoteInit(...){
    RuntimeInit.commonInit();
    //進入native層
    ZygoteInit.nativeZygoteInit();
    RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
}
           
//AndroidRuntime.cpp
static void com_android_internal_os_ZygoteInit_nativeZygoteInit(JNIEnv* env, jobject clazz){
    gCurRuntime->onZygoteInit();
}
           
//app_main.cpp
virtual void onZygoteInit()
{
    //擷取單例
    sp<ProcessState> proc = ProcessState::self();
    //在這裡啟動了binder線程池
    proc->startThreadPool();
}
           
//ProcessState.cpp
sp<ProcessState> ProcessState::self()
{
    //單例模式,傳回ProcessState對象
    if (gProcess != NULL) {
        return gProcess;
    }
    gProcess = new ProcessState("/dev/binder");
    return gProcess;
}

//ProcessState構造函數
ProcessState::ProcessState(const char *driver)
    : mDriverName(String8(driver))
        , mDriverFD(open_driver(driver)) //打開binder驅動
        ,//...
{
    if (mDriverFD >= 0) {
        //mmap是一種記憶體映射檔案的方法,把mDriverFD映射到目前的記憶體空間
        mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, 
                        MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
    }
}

//啟動了binder線程池
void ProcessState::startThreadPool()
{
    if (!mThreadPoolStarted) {
        mThreadPoolStarted = true;
        spawnPooledThread(true);
    }
}

void ProcessState::spawnPooledThread(bool isMain)
{
    if (mThreadPoolStarted) {
        //建立線程名字"Binder:${pid}_${自增數字}"
        String8 name = makeBinderThreadName();
        sp<Thread> t = new PoolThread(isMain);
        //運作binder線程
        t->run(name.string());
    }
}
           

ProcessState.cpp檔案裡面有兩個宏定義值得注意一下(感興趣的可以看下這個連結):

  • 一次Binder通信最大可以傳輸的大小是 1MB-4KB*2
  • #define BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2)
  • binder驅動的檔案描述符fd被限制了最大線程數15
  • #define DEFAULT_MAX_BINDER_THREADS 15

我們看下binder線程PoolThread長啥樣,

class PoolThread : public Thread
{
public:
    explicit PoolThread(bool isMain)
        : mIsMain(isMain){}
protected:
    virtual bool threadLoop()
    {	//把binder線程注冊進binder驅動程式的線程池中
        IPCThreadState::self()->joinThreadPool(mIsMain);
        return false;
    }
    
    const bool mIsMain;
};
           
//IPCThreadState.cpp
void IPCThreadState::joinThreadPool(bool isMain)
{
    //向binder驅動寫資料:進入死循環
    mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);
    status_t result;
    do {
        //進入死循環,等待指令的到來
        result = getAndExecuteCommand();
    } while (result != -ECONNREFUSED && result != -EBADF);
    //向binder驅動寫資料:退出死循環
    mOut.writeInt32(BC_EXIT_LOOPER);
}

status_t IPCThreadState::getAndExecuteCommand()
{
    //從binder驅動讀資料,得到指令
    cmd = mIn.readInt32();
    //執行指令
    result = executeCommand(cmd);
    return result;
}
           

梳理一下binder的啟動過程:

  • 打開binder驅動
  • 映射記憶體,配置設定緩沖區
  • 運作binder線程,進入死循環,等待指令

三、 走入Binder

1. 什麼是Binder驅動

Binder驅動是Android專用的,但底層的驅動架構與Linux驅動一樣。binder驅動在以misc裝置進行注冊,作為虛拟字元裝置,沒有直接操作硬體,隻是對裝置記憶體的處理。主要是驅動裝置的初始化(binder_init),打開 (binder_open),映射(binder_mmap),資料操作(binder_ioctl)。

Android Framework系列(從系統開機到應用啟動篇)

2. Binder通信原理

Binder通信采用C/S架構,從元件視角來說,包含Client、Server、ServiceManager以及binder驅動,其中ServiceManager用于管理系統中的各種服務。架構圖如下所示:

Android Framework系列(從系統開機到應用啟動篇)

可以看出無論是注冊服務和擷取服務的過程都需要ServiceManager,需要注意的是此處的Service Manager是指Native層的ServiceManager(C++),并非指framework層的ServiceManager(Java)。ServiceManager是整個Binder通信機制的大管家,是Android程序間通信機制Binder的守護程序,要掌握Binder機制,首先需要了解系統是如何首次啟動Service Manager。當Service Manager啟動之後,Client端和Server端通信時都需要先擷取Service Manager接口,才能開始通信服務。

圖中Client/Server/ServiceManage之間的互相通信都是基于Binder機制。既然基于Binder機制通信,那麼同樣也是C/S架構,則圖中的3大步驟都有相應的Client端與Server端。

  • 注冊服務(addService):Server程序要先注冊Service到ServiceManager。該過程:Server是用戶端,ServiceManager是服務端。
  • 擷取服務(getService):Client程序使用某個Service前,須先向ServiceManager中擷取相應的Service。該過程:Client是用戶端,ServiceManager是服務端。
  • 使用服務:Client根據得到的Service資訊建立與Service所在的Server程序通信的通路,然後就可以直接與Service互動。該過程:client是用戶端,server是服務端。

圖中的Client,Server,Service Manager之間互動都是虛線表示,是由于它們彼此之間不是直接互動的,而是都通過與Binder驅動進行互動的,進而實作IPC通信方式。其中Binder驅動位于核心空間,Client,Server,Service Manager位于使用者空間。Binder驅動和Service Manager可以看做是Android平台的基礎架構,而Client和Server是Android的應用層,開發人員隻需自定義實作client、Server端,借助Android的基本平台架構便可以直接進行IPC通信。

3. Binder記憶體機制

Android Framework系列(從系統開機到應用啟動篇)

虛拟程序位址空間(vm_area_struct)和虛拟核心位址空間(vm_struct)都映射到同一塊實體記憶體空間。當Client端與Server端發送資料時,Client(作為資料發送端)先從自己的程序空間把IPC通信資料copy_from_user拷貝到核心空間,而Server端(作為資料接收端)與核心共享資料,不再需要拷貝資料,而是通過記憶體位址空間的偏移量,即可獲悉記憶體位址,整個過程隻發生一次記憶體拷貝。一般地做法,需要Client端程序空間拷貝到核心空間,再由核心空間拷貝到Server程序空間,會發生兩次拷貝。

對于程序和核心虛拟位址映射到同一個實體記憶體的操作是發生在資料接收端,而資料發送端還是需要将使用者态的資料複制到核心态。到此,可能有讀者會好奇,為何不直接讓發送端和接收端直接映射到同一個實體空間,那樣就連一次複制的操作都不需要了,0次複制操作那就與Linux标準核心的共享記憶體的IPC機制沒有差別了,對于共享記憶體雖然效率高,但是對于多程序的同步問題比較複雜,而管道/消息隊列等IPC需要複制2兩次,效率較低。這裡就不先展開讨論Linux現有的各種IPC機制跟Binder的詳細對比,總之Android選擇Binder的基于速度和安全性的考慮。

下面這圖是從Binder在程序間資料通信的流程圖,從圖中更能明了Binder的記憶體轉移關系。

Android Framework系列(從系統開機到應用啟動篇)

五、常見問題解答

  • Zygote的跨程序通信沒有使用binder,而是socket,是以應用程式程序的binder機制不是繼承而來,而是程序建立後自己啟動的。
  • Zygote跨程序通信之是以用socket而不是binder,是因為binder通信是多線程的,而Zygote需要在單線程狀态下fork子程序來避免死鎖問題。
  • PMS、AMS等系統服務啟動後會調用ServiceManager.addService()注冊,然後運作在自己的工作線程。

參考文章:

  • gityuan的binder系列
  • 哈利迪的Android系統啟動和一圖摸清應用程序的啟動

    Android系統架構