2018年5月10日 星期四

RWebRTC - Android 系统启动流程 #3

原文 https://github.com/RWebRTC/Blog/issues/3

Android系統啟動

對於Android系統整個啟動過程來說,基本可以劃分成三個階段:Bootloader引導,Linux內核啟動,Android啟動。如下的流程圖可以看出三個階段的執行順序和聯繫(一張圖看不懂就看三張圖)。
112356615
112356709
2848265-3

Bootloader引導

BootLoader是在操作系統運行之前運行的一段程序,它可以使系統的軟硬件環境帶到一個合適的狀態,為運行操作系統做好準備。它的任務就是把OS拉起來運行,啟動Linux內核,我們對這一塊的內容並不太注意。

Linux內核啟動

Linex內核啟動後會初始化環境/驅動等,接著會啟動init的進程。

1.Init進程的啟動

init進程,它是一個由內核啟動的用戶級進程。內核自行啟動(已經被載入內存,開始運行,並已初始化所有的設備驅動程序和數據結構等)之後,就通過啟動一個用戶級程序init的方式,啟動init進程。
啟動過程是代碼system\core\init\init.cpp的主要函數的執行過程。在函數中執行了:文件夾建立,掛載,rc文件解析,屬性設置,啟動服務,執行動作,socket監聽等。在rc文件解析中會啟動ServiceManager服務和Zygote進程。

2. ServiceManager服務

ServiceManager用來管理系統中所有的binder服務,不管是本地的c ++實現的還是java語言實現的都需要這個進程來統一管理,最主要的管理就是,註冊添加服務,獲取服務。所有的服務使用前都必須先在ServiceManager中進行註冊。

3. Zygote進程的啟動

Zygote進程啟動才算建立起真正的Android的運行空間。不過過程不會直接啟動Zygote進程,而是使用app_process命令來通過Android Runtime來啟動,Android Runtime會啟動第一個Davlik虛擬機,並調用Zygote的主要方法,啟動服務.dir / system / core / rootdir / init.zygote32.rc:
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server

Android的啟動

1. Zygote啟動

Zygote從框架/ base / cmds / app_process / app_main.cpp中的主開始,並建立Android運行時。
//建立 Android runtime
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
// 然后调用 Java 的 ZygoteInit
// dir/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
// 随后
//登记Listen端口
registerZygoteSocket();
//启动SystemServer服务
startSystemServer();

2.系統服務器啟動

在上一步中調用了startSystemServer,Zygote上叉了一個進程:com.android.server.SystemServer。於是Java中的SystemServer就建立起來了,SystemServer的主函數中會啟動一些服務:
startBootstrapServices();
startCoreServices();
startOtherServices();
在startBootstrapServices()中看到
mActivityManagerService = mSystemServiceManager.startService(ActivityManagerService.Lifecycle.class).getService();
ActivityManagerService.Lifecycle是ActivityManagerService的靜態內部類,持有ActivityManagerService實例。所以ActivityManagerService是在這裡被啟動的來。
當然還有很多的其他服務(PackageManagerService,PowerManagerService,DisplayManagerService,BatteryService等)也都會被啟動起來。
在所有的服務啟動完成後,會調用Service.systemReady()來通知各對應的服務,系統已經就緒。

3.ActivityManagerService啟動

在ActivityManagerService的systemReady()方法中有:
startHomeActivityLocked();
mStackSupervisor.startHomeActivity()
這樣主頁程序被啟動起來,整個機器人的啟動過程就完成了,進入用戶界面。

霸氣的分割線

Android的init進程啟動過程

0.前言

Android中的內核啟動後,內核會啟動第一個用戶級別的進程:init,它是一個由內核啟動的第一個用戶級進程。我們可以通過adb shell ps | grep init來查看他的pid為1.接下來從源碼的角度看看的init進程啟動的時候做了什麼?
init進程的源碼在android源碼的目錄中。我們看到該目錄下有一個Android.mk文件,至少看到如下內容,告訴我們會生成一個init的可執行文件。
LOCAL_MODULE:= init
include $(BUILD_EXECUTABLE)
而init的入口主函數是在init.cpp文件中定義的。

1.入口

1.命令行解析

if (!strcmp(basename(argv[0]), "ueventd")) {    
      return ueventd_main(argc, argv);
}
if (!strcmp(basename(argv[0]), "watchdogd")) {    
      return watchdogd_main(argc, argv);
}
看門狗和uevent命令已經集成到了init,它們在/ sbin目錄,是一個鏈接文件,直接鏈接到/ init,所以當執行/ sbin / eventd或/ sbin / watchdogd時,將會進入對應的ueventd_main或watchdogd_main入口點.ueventd守護進程負責解析/ueventd.rc文件,並創建相應的設備結點等.watchdogd守護進程負責定時向“/ dev / watchdog”執行寫操作,以判斷系統是否正常運行。這兩個進程不是本文討論的重點,所以先忽略。

2.掛載根文件系統的目錄

mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
mkdir("/dev/pts", 0755);
mkdir("/dev/socket", 0755);
mount("devpts", "/dev/pts", "devpts", 0, NULL);
mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
mount("sysfs", "/sys", "sysfs", 0, NULL);
mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);
mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));
分別掛載proc和和sysfs文件系統到/ proc和/ sys目錄
close(open(“/ dev / .booting”,O_WRONLY | O_CREAT | O_CLOEXEC,0000)); 在/ dev目錄創建一個空文件.booting來表示正在執行
初始化InitKernelLogging(argv); 重定向標準輸入,標準輸出,標準錯誤輸出到/ dev / null 
selinux_initialize(is_first_stage)加載SELinux策略,後面有一些初始化文件上下文的操作等

3.解析init.rc文件

Parser& parser Parser::GetInstance();
parser.AddSectionParser("service",std::make_unique());
parser.AddSectionParser("on", std::make_unique());
parser.AddSectionParser("import", std::make_unique());
parser.ParseConfig("/init.rc");
  • 3.1 init.rc文件是以塊(section)為單位組織的,一個section包含多行.section分為兩大類:分別為“服務(service)”和“行為(action)”。
  • 3.2“服務”塊以關鍵字“service”開始,表示啟動某個進程的方式和參數,“行為”塊以關鍵字“on”開始,表示一堆命令的集合。上“或”上“或”服務“開始,直到下一個”,“結束,中間所有行都屬於這個”塊“。
  • 3.3上面在解析init.rc文件時使用了Parser類(在init目錄下的init_parser.h中定義),初始ServiceParser用來解析“service”塊,ActionParser用來解析“on”塊,ImportParser用來解析“ import“塊,”import“是用來引入一個init配置文件,來擴展當前配置的。
  • 3.4解析器解析init.rc文件,/ init.rc文件是dir / system / core / rootdir / init.rc。
  • 3.5 dir / system / core / init / readme.txt中對init文件中的所有關鍵字做了介紹,主要包含了操作,命令,服務,選項和導入等
  • 3.6在ParseConfig解析完init腳本後,init會依次執行幾個重要的階段:
    3.6.1在早期階段
      am.QueueEventTrigger("early-init"); 执行 on early-init 内容,主要包括 start ueventd 等
    
    3.6.2。在init階段
      am.QueueEventTrigger("init"); 执行 on init 内容,主要包括 创建/挂载一些目录,以及 symlink 等
    
    3.6.3。在充電器/晚期初始階段
      ```
       // Don't mount filesystems or start core system services in charger mode.
       std::string bootmode = property_get("ro.bootmode");
       if (bootmode == "charger") {
           am.QueueEventTrigger("charger");
       } else {
           am.QueueEventTrigger("late-init");
       }
      ```
    
    如果是充電模式下啟動就會執行充電器內容,否則執行on late-init內容,在init.rc的late-init中看到很多trigger(觸發器),用於執行對應的Action。
      ```
      trigger late-fs
      
      # Now we can mount /data. File encryption requires keymaster to decrypt
      # /data, which in turn can only be loaded when system properties are present.
      trigger post-fs-data
      
      # Load persist properties and override properties (if enabled) from /data.
      trigger load_persist_props_action
      
      # Remove a file to wake up anything waiting for firmware.
      trigger firmware_mounts_complete
      
      trigger early-boot
      trigger boot
      ```
    
    從最後兩行可以看出,late-init觸發了早啟動和開機兩個Action。
    3.6.4。在啟動階段在開機的最後class_start核心會啟動類為核心的服務,這些服務包括ueventd,logd,healthd,adbd(禁用),lmkd(LowMemoryKiller),servicemanager,vold,debuggerd,surfaceflinger,bootanim(禁用)等
    3.6.5。主服務的啟動
    在主函數後面的while循環中,調用execute_one_command依次執行操作隊列中的命令
      ```
      while (true) {
          if (!waiting_for_exec) {
              am.ExecuteOneCommand();
              restart_processes();
          }
      }
      ```
    
    在/init.rc的開頭部分
      ```
      import /init.environ.rc
      import /init.usb.rc
      import /init.${ro.hardware}.rc
      import /init.usb.configfs.rc
      import /init.${ro.zygote}.rc
      ```
    
    通過ro.zygote的屬性導入對應的zygote的rc文件,通過adb shell getprop ro.zygote查看得到zygote64_32,所以導入的是/init.zygote64_32.rc文件,該文件中定義的zygote如下:
      ```
      service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
      class main
      priority -20
      socket zygote stream 660 root system
      onrestart write /sys/android_power/request_state wake
      onrestart write /sys/power/state on
      onrestart restart audioserver
      onrestart restart cameraserver
      onrestart restart media
      onrestart restart netd
      writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
      ```
    
    可以看到zygote的class是main,它是在on nonencrypted時被啟動的
      ```
      on nonencrypted
          # A/B update verifier that marks a successful boot.
          exec - root -- /system/bin/update_verifier nonencrypted
          class_start main
          class_start late_start
      ```
    
    注:但在Android 7.0中,對該機製做了一些改變。
    單一的init * .rc,被拆分,服務根據其二進製文件的位置(/ system,/ vendor,/ odm)定義到對應分區的etc / init目錄中,每個服務一個rc文件。與該服務相關的觸發器,操作等也定義在同一個rc文件中。
      /system/etc/init,包含系统核心服务的定义,如 SurfaceFlinger、MediaServer、Logcatd 等。
      /vendor/etc/init, SOC 厂商针对 SOC 核心功能定义的一些服务。比如高通、MTK 某一款 SOC 的相关的服务。
      /odm/etc/init,OEM/ODM 厂商如小米、华为、OPP 其产品所使用的外设以及差异化功能相关的服务。
    
    查看Android 7.0虛擬機的/ system / etc / init如下
    2848265-c235da5cec4a1208
    上面的ServiceManager這個服務也從init.rc中拆分出來了。

4.啟動完成

至此,init進程已經啟動完成,一些重要的服務如核心服務和主服務也都啟動起來,並啟動了zygote(/ system / bin / app_process64)進程,zygote初始化時會創建虛擬機,啟動SystemServer等,它的啟動過程也是非常複雜。

霸氣的分割線

Zygote的啟動過程

0.前言

上節文章的最後說到了init以service的方式啟動了Zygote進程。這節文章主要講Zygote進程的啟動流程。
對於Zygote進程的描述如下:
在Android中,zygote是整個系統創建新進程的核心.Zygote進程在內部會先啟動Dalvik虛擬機,繼而加載一些必要的系統資源和系統類,最後進入一種監聽狀態。在後續的運作中,當其他系統模塊(比如AMS)希望創建新進程時,只需向Zygote進程發出請求,Zygote進程監聽到該請求後,會相應地“分裂”出新的進程,於是這個新進程在初生之時,就先天具有了自己的Dalvik虛擬機以及系統資源。

1.啟動流程

/init.zygote64_32.rc文件中啟動Zygote的內容如下:
    service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
    class main
    priority -20
    socket zygote stream 660 root system
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on
    onrestart restart audioserver
    onrestart restart cameraserver
    onrestart restart media
    onrestart restart netd
    writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
在dir / system / core / rootdir /目錄下可以看到init.zygote32.rc,init.zygote32_64.rc,init.zygote64.rc,init.zygote64_32.rc等文件,這是因為Android5.0開始支持64位的編譯,所以Zygote進程本身也有32位和64位版本。
從上面定義看到Zygote進程的可執行文件是frameworks / base / cmds / app_process / app_main.cpp。
接下來分析主函數的流程。
1.1。創建AppRuntime對象
AppRuntime是在app_process中定義的類,繼承了系統的AndroidRuntime類,AndroidRuntime類的主要作用是創建和初始化虛擬機。
1.2。解析啟動參數
  while (i < argc) {
    const char* arg = argv[i++];
    if (strcmp(arg, "--zygote") == 0) {
        zygote = true;
        niceName = ZYGOTE_NICE_NAME;
    } else if (strcmp(arg, "--start-system-server") == 0) {
        startSystemServer = true;
    } else if (strcmp(arg, "--application") == 0) {
        application = true;
    } else if (strncmp(arg, "--nice-name=", 12) == 0) {
        niceName.setTo(arg + 12);
    } else if (strncmp(arg, "--", 2) != 0) {
        className.setTo(arg);
        break;
    } else {
        --i;
        break;
    }
}
從init.rc文件中傳入的參數是-Xzygote / system / bin --zygote --start-system-server --socket-name = zygote,解析後:
zygote = true;
niceName = zygoe;
startSystemServer = true;
1.3。執行ZygoteInit類
if (zygote) {
    runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
} else if (className) {
    runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
} else {
    fprintf(stderr, "Error: no class name or --zygote supplied.\n");
    app_usage();
    LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
    return 10;
}
Zygote經過解析參數後為true,所以會執行runtime.start(“com.android.internal.os.ZygoteInit”,args,zygote); 
AndroidRuntime的start方法實現在dir / frameworks / base / core / jni / AndroidRuntime.cpp中,
/*

 * Start the Android runtime.  This involves starting the virtual machine
 * and calling the "static void main(String[] args)" method in the class
 * named by "className".
 *
 * Passes the main function two arguments, the class name and the specified
 * options string.
 */
void AndroidRuntime::start(const char* className, const Vector& options, bool zygote)
{
    // 启动虚拟机,注册jni等
    char* slashClassName = toSlashClassName(className);
    jclass startClass = env->FindClass(slashClassName);
    if (startClass == NULL) {
    ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
    /* keep going */
    } else {
        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
            "([Ljava/lang/String;)V");
        if (startMeth == NULL) {
            ALOGE("JavaVM unable to find main() in '%s'\n", className);
            /* keep going */
        } else {
            env->CallStaticVoidMethod(startClass, startMeth, strArray);

#if 0
        if (env->ExceptionCheck())
            threadExitUncaughtException(env);
#endif
        }
    }
      ...
}
這個方法會啟動Android運行環境,意味著它會啟動Android虛擬機,並調用參數中className中的主方法。
從傳入的com.android.internal.os.ZygoteInit類中找到主函數,即調用ZygoteInit。 java類中的main方法.AndroidRuntime及之前的方法都是native的方法,而此刻調用的ZygoteInit.main方法是java的方法,到這裡我們就進入了java的世界。
調用示意圖如下:
2848265-da92b5d71fba5c28
1.4。ZygoteInit的主要方法
ZygoteInit定義在dir / frameworks / base / core / java / com / android / internal / os / ZygoteInit.java中。
public static void main(String argv[]) {
    try {
        ...
        registerZygoteSocket(socketName);
        preload();
        if (startSystemServer) {
            startSystemServer(abiList, socketName);
        }
        runSelectLoop(abiList);
        ...
    } catch (MethodAndArgsCaller caller) {
        caller.run();
    } catch (RuntimeException ex) {
        Log.e(TAG, "Zygote died with exception", ex);
        closeServerSocket();
        throw ex;
    }
}
主要工作:
  • 註冊受精卵的插座監聽端口,應用接收啟動應用程序的消息
  • 調用預載()方法加載系統資源,包括預加載類,框架資源等
  • 調用startSystemServer()方法啟動SystemServer進程
  • 調用runSelectLoop()方法進入監聽和接收消息循環
可見SystemServer是受精卵啟動的第一個進程。
以上學習了Android系統的啟動流程。

沒有留言: