2018年5月10日 星期四

Android Framework學習(一)之init進程解析

原文 https://blog.csdn.net/u012124438/article/details/70990816

init進程是Android系統中用戶空間的第一個進程,它被賦予了很多極其重要的工作職責,init進程相關源碼位於system/core/init,本篇博客我們就一起來學習init進程(基於Android 7.0 )。

init入口函數分析

init的入口函數為main,位於system/core/init/init.cpp
int main(int argc, char** argv) {
    if (!strcmp(basename(argv[0]), "ueventd")) {
        return ueventd_main(argc, argv);
    }

    if (!strcmp(basename(argv[0]), "watchdogd")) {
        return watchdogd_main(argc, argv);
    }

    // Clear the umask.
    umask(0);

    add_environment("PATH", _PATH_DEFPATH);

    bool is_first_stage = (argc == 1) || (strcmp(argv[1], "--second-stage") != 0);

    // Get the basic filesystem setup we need put together in the initramdisk
    // on / and then we'll let the rc file figure out the rest.
    //1.创建一些文件夹,并挂载设备,这些都是与Linux相关
    if (is_first_stage) {
        mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
        mkdir("/dev/pts", 0755);
        mkdir("/dev/socket", 0755);
        mount("devpts", "/dev/pts", "devpts", 0, NULL);
        #define MAKE_STR(x) __STRING(x)
        mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
        mount("sysfs", "/sys", "sysfs", 0, NULL);
    }

    // We must have some place other than / to create the device nodes for
    // kmsg and null, otherwise we won't be able to remount / read-only
    // later on. Now that tmpfs is mounted on /dev, we can actually talk
    // to the outside world.
    //2.重定向标准输入,输出,错误输出到/dev/_null_
    open_devnull_stdio();
    3.初始化内核log系统 
    klog_init();
    klog_set_level(KLOG_NOTICE_LEVEL);

    NOTICE("init %s started!\n", is_first_stage ? "first stage" : "second stage");

    if (!is_first_stage) {
        // Indicate that booting is in progress to background fw loaders, etc.
        close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
        //4.初始化和属性相关资源
        property_init();

        // If arguments are passed both on the command line and in DT,
        // properties set in DT always have priority over the command-line ones.
        process_kernel_dt();
        process_kernel_cmdline();

        // Propagate the kernel variables to internal variables
        // used by init as well as the current required properties.
        export_kernel_boot_props();
    }

    // Set up SELinux, including loading the SELinux policy if we're in the kernel domain.
    5.完成SELinux相关工作
    selinux_initialize(is_first_stage);

    // If we're in the kernel domain, re-exec init to transition to the init domain now
    // that the SELinux policy has been loaded.
    if (is_first_stage) {
        6.重新设置属性
        if (restorecon("/init") == -1) {
            ERROR("restorecon failed: %s\n", strerror(errno));
            security_failure();
        }
        char* path = argv[0];
        char* args[] = { path, const_cast<char*>("--second-stage"), nullptr };
        if (execv(path, args) == -1) {
            ERROR("execv(\"%s\") failed: %s\n", path, strerror(errno));
            security_failure();
        }
    }

    // These directories were necessarily created before initial policy load
    // and therefore need their security context restored to the proper value.
    // This must happen before /dev is populated by ueventd.
    NOTICE("Running restorecon...\n");
    restorecon("/dev");
    restorecon("/dev/socket");
    restorecon("/dev/__properties__");
    restorecon("/property_contexts");
    restorecon_recursive("/sys");
    7.创建epoll句柄 
    epoll_fd = epoll_create1(EPOLL_CLOEXEC);
    if (epoll_fd == -1) {
        ERROR("epoll_create1 failed: %s\n", strerror(errno));
        exit(1);
    }
    8.装载子进程信号处理器
    signal_handler_init();

    property_load_boot_defaults();
    export_oem_lock_status();
    //9.启动属性服务
    start_property_service();

    const BuiltinFunctionMap function_map;
    Action::set_function_map(&function_map);

    Parser& parser = Parser::GetInstance();
    parser.AddSectionParser("service",std::make_unique());
    parser.AddSectionParser("on", std::make_unique());
    parser.AddSectionParser("import", std::make_unique());
    //10.解析init.rc配置文件
    parser.ParseConfig("/init.rc");

    ActionManager& am = ActionManager::GetInstance();

    am.QueueEventTrigger("early-init");

    // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
    am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
    // ... so that we can start queuing up actions that require stuff from /dev.
    am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
    am.QueueBuiltinAction(set_mmap_rnd_bits_action, "set_mmap_rnd_bits");
    am.QueueBuiltinAction(keychord_init_action, "keychord_init");
    am.QueueBuiltinAction(console_init_action, "console_init");

    // Trigger all the boot actions to get us started.
    am.QueueEventTrigger("init");

    // Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
    // wasn't ready immediately after wait_for_coldboot_done
    am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");

    // 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");
    }

    // Run all property triggers based on current state of the properties.
    am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");

    while (true) {
        if (!waiting_for_exec) {
            am.ExecuteOneCommand();
            restart_processes();
        }

        int timeout = -1;
        if (process_needs_restart) {
            timeout = (process_needs_restart - gettime()) * 1000;
            if (timeout < 0)
                timeout = 0;
        }

        if (am.HasMoreCommands()) {
            timeout = 0;
        }

        bootchart_sample(&timeout);

        epoll_event ev;
        int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));
        if (nr == -1) {
            ERROR("epoll_wait failed: %s\n", strerror(errno));
        } else if (nr == 1) {
            ((void (*)()) ev.data.ptr)();
        }
    }

    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
從上面代碼中可以精簡歸納init的main方法做的事情:
1.創建文件系統目錄並掛載相關的文件系統
2.屏蔽標準的輸入輸出
3.初始化內核log系統 
4.調用property_init初始化屬性相關的資源
5.完成SELinux相關工作
6.重新設置屬性
7.創建epoll句柄 
8.裝載子進程信號處理器
9.通過property_start_service啟動屬性服務
10.通過parser.ParseConfig(“/init.rc”)來解析init.rc 
接下來對上述部分步驟,進行詳細解析。

1.創建文件系統目錄並掛載相關的文件系統

//清除屏蔽字(file mode creation mask),保证新建的目录的访问权限不受屏蔽字影响。
umask(0);

add_environment("PATH", _PATH_DEFPATH);

bool is_first_stage = (argc == 1) || (strcmp(argv[1], "--second-stage") != 0);

// Get the basic filesystem setup we need put together in the initramdisk
if (is_first_stage) {
    mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
    mkdir("/dev/pts", 0755);
    mkdir("/dev/socket", 0755);
    mount("devpts", "/dev/pts", "devpts", 0, NULL);
    #define MAKE_STR(x) __STRING(x)
    mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
    mount("sysfs", "/sys", "sysfs", 0, NULL);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
該部分主要用於創建和掛載啟動所需的文件目錄。 
需要注意的是,在編譯Android系統源碼時,在生成的根文件系統中,並不存在這些目錄,它們是系統運行時的目錄,即當系統終止時,就會消失。
在init初始化過程中,Android分別掛載了tmpfs,devpts,proc,sysfs這4類文件系統。

2.屏蔽標準的輸入輸出

open_devnull_stdio();
  • 1
前文生成/dev目錄後,init進程將調用open_devnull_stdio函數,屏蔽標準的輸入輸出。 
open_devnull_stdio函數會在/dev目錄下生成null設備節點文件,並將標準輸入、標準輸出、標準錯誤輸出全部重定向到null設備中。
void open_devnull_stdio(void)
{
    // Try to avoid the mknod() call if we can. Since SELinux makes
    // a /dev/null replacement available for free, let's use it.
    int fd = open("/sys/fs/selinux/null", O_RDWR);
    if (fd == -1) {
        // OOPS, /sys/fs/selinux/null isn't available, likely because
        // /sys/fs/selinux isn't mounted. Fall back to mknod.
        static const char *name = "/dev/__null__";
        if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) {
            fd = open(name, O_RDWR);
            unlink(name);
        }
        if (fd == -1) {
            exit(1);
        }
    }

    dup2(fd, 0);
    dup2(fd, 1);
    dup2(fd, 2);
    if (fd > 2) {
        close(fd);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
open_devnull_stdio函數定義於system/core/init/util.cpp中。
這裡需要說明的是,dup2函數的作用是用來複製一個文件的描述符,通常用來重定向進程的stdin、stdout和stderr。它的函數原形是:
int dup2(int oldfd,int targetfd)
該函數執行後,targetfd將變成oldfd的複製品。
因此上述過程其實就是:創建出null設備後,將0、1、2綁定到null設備上。因此init進程調用open_devnull_stdio函數後,通過標準的輸入輸出無法輸出信息。

4.初始化屬性域

if (!is_first_stage) {
    .......
    property_init();
    .......
}
  • 1
  • 2
  • 3
  • 4
  • 5
調用property_init初始化屬性域。在Android平台中,為了讓運行中的所有進程共享系統運行時所需要的各種設置值,系統開闢了屬性存儲區域,並提供了訪問該區域的API。
需要強調的是,在init進程中有部分代碼塊以is_first_stage標誌進行區分,決定是否需要進行初始化,而is_first_stage的值,由init進程main函數的入口參數決定。其原因在於,在引入selinux機制後,有些操作必須要在內核態才能完成; 
但init進程作為android的第一個進程,又是運行在用戶態的。 
於是,最終設計為用is_first_stage進行區分init進程的運行狀態。init進程在運行的過程中,會完成從內核態到用戶態的切換。
void property_init() {
    if (__system_property_area_init()) {
        ERROR("Failed to initialize property area\n");
        exit(1);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
property_init函數定義於system/core/init/property_service.cpp中,如上面代碼所示,最終調用_system_property_area_init函數初始化屬性域。

5.完成SELinux相關工作

// Set up SELinux, including loading the SELinux policy if we're in the kernel domain.
selinux_initialize(is_first_stage);
  • 1
  • 2
init進程進程調用selinux_initialize啟動SELinux。從註釋來看,init進程的運行確實是區分用戶態和內核態的。
static void selinux_initialize(bool in_kernel_domain) {
    Timer t;

    selinux_callback cb;
    //用于打印log的回调函数
    cb.func_log = selinux_klog_callback;
    selinux_set_callback(SELINUX_CB_LOG, cb);
    //用于检查权限的回调函数
    cb.func_audit = audit_callback;
    selinux_set_callback(SELINUX_CB_AUDIT, cb);

    if (in_kernel_domain) {
        //内核态处理流程
        INFO("Loading SELinux policy...\n");
        //用于加载sepolicy文件。该函数最终将sepolicy文件传递给kernel,这样kernel就有了安全策略配置文件,后续的MAC才能开展起来。
        if (selinux_android_load_policy() < 0) {
            ERROR("failed to load policy: %s\n", strerror(errno));
            security_failure();
        }
        //内核中读取的信息
        bool kernel_enforcing = (security_getenforce() == 1);
        //命令行中得到的数据
        bool is_enforcing = selinux_is_enforcing();
        if (kernel_enforcing != is_enforcing) {
            //用于设置selinux的工作模式。selinux有两种工作模式:
            //1、”permissive”,所有的操作都被允许(即没有MAC),但是如果违反权限的话,会记录日志
            //2、”enforcing”,所有操作都会进行权限检查。在一般的终端中,应该工作于enforing模式
            if(security_setenforce(is_enforcing)) {
                ........
                //将重启进入recovery mode
                security_failure();
            }
        }
        if (write_file("/sys/fs/selinux/checkreqprot", "0") == -1) {
            security_failure();
        }

        NOTICE("(Initializing SELinux %s took %.2fs.)\n",
               is_enforcing ? "enforcing" : "non-enforcing", t.duration());
    } else {
        selinux_init_all_handles();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

6.重新設置屬性

// If we're in the kernel domain, re-exec init to transition to the init domain now that the SELinux policy has been loaded.
if (is_first_stage) {
    //按selinux policy要求,重新设置init文件属性
    if (restorecon("/init") == -1) {
        ERROR("restorecon failed: %s\n", strerror(errno));
        security_failure();
    }
    char* path = argv[0];
    char* args[] = { path, const_cast<char*>("--second-stage"), nullptr };
    //这里就是前面所说的,启动用户态的init进程,即second-stage
    if (execv(path, args) == -1) {
        ERROR("execv(\"%s\") failed: %s\n", path, strerror(errno));
        security_failure();
    }
}

// These directories were necessarily created before initial policy load
// and therefore need their security context restored to the proper value.
// This must happen before /dev is populated by ueventd.
INFO("Running restorecon...\n");
restorecon("/dev");
restorecon("/dev/socket");
restorecon("/dev/__properties__");
restorecon_recursive("/sys");
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
上述文件節點在加載Sepolicy之前已經被創建了,因此在加載完Sepolicy後,需要重新設置相關的屬性。

9.啟動配置屬性的服務端

start_property_service();
  • 1
init進程在共享內存區域中,創建並初始化屬性域。其它進程可以訪問屬性域中的值,但更改屬性值僅能在init進程中進行。這就是init進程調用start_property_service的原因。其它進程修改屬性值時,要預先向init進程提交值變更申請,然後init進程處理該申請,並修改屬性值。在訪問和修改屬性時,init進程都可以進行權限控制。
void start_property_service() {
    //创建了一个非阻塞socket
    property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0666, 0, 0, NULL);
    if (property_set_fd == -1) {
        ERROR("start_property_service socket creation failed: %s\n", strerror(errno));
        exit(1);
    }
    //调用listen函数监听property_set_fd, 于是该socket变成一个server
    listen(property_set_fd, 8);
    //监听server socket上是否有数据到来
    register_epoll_handler(property_set_fd,  handle_property_set_fd);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
我們知道,在create_socket函數返回套接字property_set_fd時,property_set_fd是一個主動連接的套接字。此時,系統假設用戶會對這個套接字調用connect函數,期待它主動與其它進程連接。
由於在服務器編程中,用戶希望這個套接字可以接受外來的連接請求,也就是被動等待用戶來連接,於是需要調用listen函數使用主動連接套接字變為被連接套接字,使得一個進程可以接受其它進程的請求,從而成為一個服務器進程。
因此,調用listen後,init進程成為一個服務進程,其它進程可以通過property_set_fd連接init進程,提交設置系統屬性的申請。
listen函數的第二個參數,涉及到一些網絡的細節。
在進程處理一個連接請求的時候,可能還存在其它的連接請求。因為TCP連接是一個過程,所以可能存在一種半連接的狀態。有時由於同時嘗試連接的用戶過多,使得服務器進程無法快速地完成連接請求。
因此,內核會在自己的進程空間里維護一個隊列,以跟踪那些已完成連接但服務器進程還沒有接手處理的用戶,或正在進行的連接的用戶。這樣的一個隊列不可能任意大,所以必須有一個上限。listen的第二個參數就是告訴內核使用這個數值作為上限。因此,init進程作為系統屬性設置的服務器,最多可以同時為8個試圖設置屬性的用戶提供服務。
在啟動配置屬性服務的最後,調用函數register_epoll_handler。該函數將利用之前創建出的epoll句柄監聽property_set_fd。當property_set_fd中有數據到來時,init進程將利用handle_property_set_fd函數進行處理。
static void handle_property_set_fd() {
    ..........
    if ((s = accept(property_set_fd, (struct sockaddr *) &addr, &addr_size)) < 0) {
        return;
    }
    ........
    r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), MSG_DONTWAIT));
    .........
    switch(msg.cmd) {
    .........
    }
    .........
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
handle_propery_set_fd函數實際上是調用accept函數監聽連接請求,接收property_set_fd中到來的數據,然後利用recv函數接受到來的數據,最後根據到來數據的類型,進行設置系統屬性等相關操作,在此不做深入分析。
介紹一下系統屬性改變的一些用途。 
在init.rc中定義了一些與屬性相關的觸發器。當某個條件相關的屬性被改變時,與該條件相關的觸發器就會被觸發。舉例來說,如下面代碼所示,debuggable屬性變為1時,將執行啟動console進程等操作。
on property:ro.debuggable=1
    # Give writes to anyone for the trace folder on debug builds.
    # The folder is used to store method traces.
    chmod 0773 /data/misc/trace
    start console
  • 1
  • 2
  • 3
  • 4
  • 5
總結一下,其它進程修改系統屬性時,大致的流程如下圖所示:其它的進程像init進程發送請求後,由init進程檢查權限後,修改共享內存區。 
這裡寫圖片描述

10.解析配置文件init.rc

init.rc是系統配置文件,位於system/core/rootdir/init.rc,Android 7.0中對init.rc文件進行了拆分,每個服務一個rc文件。
init.rc文件是在init進程啟動後執行的啟動腳本,文件中記錄著init進程需執行的操作。在Android系統中,使用init.rc和init.{ hardware }.rc兩個文件。
其中init.rc文件在Android系統運行過程中用於通用的環境設置與進程相關的定義,init.{hardware}.rc(例如,高通有init.qcom.rc,MTK有init.mediatek.rc)用於定義Android在不同平台下的特定進程和環境設置等。
init.rc文件大致分為兩大部分,一部分是以“on”關鍵字開頭的動作列表(action list):
on early-init
    # Set init and its forked children's oom_adj.
    write /proc/1/oom_score_adj -1000
    .........
    start ueventd
  • 1
  • 2
  • 3
  • 4
  • 5
另一部分是以“service”關鍵字開頭的服務列表(service list):
service ueventd /sbin/ueventd
    class core
    critical
    seclabel u:r:ueventd:s0
  • 1
  • 2
  • 3
  • 4
動作列表用於創建所需目錄,以及為某些特定文件指定權限,而服務列表用來記錄init進程需要啟動的一些子進程。如上面代碼所示,service關鍵字後的第一個字符串表示服務(子進程)的名稱,第二個字符串表示服務的執行路徑。
接下來,我們從ParseConfig函數入手,逐步分析整個解析過程(函數定義於system/core/init/ Init_parser.cpp中):
bool Parser::ParseConfig(const std::string& path) {
    if (is_dir(path.c_str())) {
        //传入参数为目录地址
        return ParseConfigDir(path);
    }
    //传入参数为文件地址
    return ParseConfigFile(path);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
bool Parser::ParseConfigDir(const std::string& path) {
    ...........
    std::unique_ptr<DIR, int(*)(DIR*)> config_dir(opendir(path.c_str()), closedir);
    ..........
    //看起来很复杂,其实就是递归目录
    while ((current_file = readdir(config_dir.get()))) {
        std::string current_path = android::base::StringPrintf("%s/%s", path.c_str(), current_file->d_name);
        if (current_file->d_type == DT_REG) {
            //最终还是靠ParseConfigFile来解析实际的文件
            if (!ParseConfigFile(current_path)) {
                .............
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
從上面的代碼可以看出,解析init.rc文件的函數是ParseConfigFile:
bool Parser::ParseConfigFile(const std::string& path) {
    INFO("Parsing file %s...\n", path.c_str());
    Timer t;
    std::string data;
    //读取路径指定文件中的内容,保存为字符串形式
    if (!read_file(path.c_str(), &data)) {
        return false;
    }

    data.push_back('\n'); // TODO: fix parse_config.
    //解析获取的字符串
    ParseData(path, data);
    for (const auto& sp : section_parsers_) {
        sp.second->EndFile(path);
    }

    // Turning this on and letting the INFO logging be discarded adds 0.2s to
    // Nexus 9 boot time, so it's disabled by default.
    if (false) DumpState();

    NOTICE("(Parsing %s took %.2fs.)\n", path.c_str(), t.duration());
    return true;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
ParseData函數定義於system/core/init/init_parser.cpp中,根據關鍵字解析出服務和動作。動作與服務會以鍊錶節點的形式註冊到service_list與action_list中,service_list與action_list是init進程中聲明的全局結構體
void Parser::ParseData(const std::string& filename, const std::string& data) {
    .......
    parse_state state;
    .......
    std::vector<std::string> args;

    for (;;) {
        //next_token以行为单位分割参数传递过来的字符串
        //最先走到T_TEXT分支
        switch (next_token(&state)) {
        case T_EOF:
            if (section_parser) {
                //EOF,解析结束
                section_parser->EndSection();
            }
            return;
        case T_NEWLINE:
            state.line++;
            if (args.empty()) {
                break;
            }
            //创建parser时,会为init.rc中以service,on,import开头的都定义了对应的解析parser 
            //这里就是根据第一个参数,判断是否有对应的parser
            if (section_parsers_.count(args[0])) {
                if (section_parser) {
                    //结束上一个parser的工作,将构造出的对象加入到对应的service_list与action_list中
                    section_parser->EndSection();
                }
                //获取参数对应的parser
                section_parser = section_parsers_[args[0]].get();
                std::string ret_err;
                //调用实际parser的ParseSection函数
                if (!section_parser->ParseSection(args, &ret_err)) {
                    parse_error(&state, "%s\n", ret_err.c_str());
                    section_parser = nullptr;
                }
            } else if (section_parser) {
                std::string ret_err;
                //如果第一个参数不是service,on,import
                //则调用前一个parser的ParseLineSection函数
                //这里相当于解析一个参数块的子项
                if (!section_parser->ParseLineSection(args, state.filename, state.line, &ret_err)) {
                    parse_error(&state, "%s\n", ret_err.c_str());
                }
            }
            //清空本次解析的数据
            args.clear();
            break;
        case T_TEXT:
            //将本次解析的内容写入到args中
            args.emplace_back(state.text);
            break;
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
這裡的解析看起來比較複雜,在6.0以前的版本中,整個解析是面向過程的。init進程統一調用一個函數來進行解析,然後在該函數中利用switch-case的形式,根據解析的內容進行相應的處理。 
在Android 7.0中,為了更好地封裝及面向對象,對於不同的關鍵字定義了不同的parser對象,每個對象通過多態實現自己的解析操作。
在init進程main函數中,創建各種parser的代碼如下:
...........
Parser& parser = Parser::GetInstance();
parser.AddSectionParser("service",std::make_unique());
parser.AddSectionParser("on", std::make_unique());
parser.AddSectionParser("import", std::make_unique());
...........
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
看看三個Parser的定義:
class ServiceParser : public SectionParser {......}
class ActionParser : public SectionParser {......}
class ImportParser : public SectionParser {.......}
  • 1
  • 2
  • 3
可以看到三個Parser均是繼承SectionParser,具體的實現各有不同,我們以​​比較常用的ServiceParser和ActionParser為例
ServiceParser 
ServiceParser定義於system/core/init/service.cpp中。從前面的代碼,我們知道,解析一個service塊,首先需要調用ParseSection函數,接著利用ParseLineSection處理子塊,解析完所有數據後,調用EndSection。 
因此,我們著重看看ServiceParser的這三個函數:
bool ServiceParser::ParseSection(.....) {
    .......
    const std::string& name = args[1];
    .......
    std::vector<std::string> str_args(args.begin() + 2, args.end());
    //主要根据参数,构造出一个service对象
    service_ = std::make_unique(name, "default", str_args);
    return true;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
//注意这里已经在解析子项了
bool ServiceParser::ParseLineSection(......) const {
    //调用service对象的HandleLine
    return service_ ? service_->HandleLine(args, err) : false;
}
  • 1
  • 2
  • 3
  • 4
  • 5
bool Service::HandleLine(.....) {
    ........
    //OptionHandlerMap继承自keywordMap
    static const OptionHandlerMap handler_map;
    //根据子项的内容,找到对应的handler函数
    //FindFunction定义于keyword模块中,FindFunction方法利用子类生成对应的map中,然后通过通用的查找方法,即比较键值找到对应的处理函数
    auto handler = handler_map.FindFunction(args[0], args.size() - 1, err);

    if (!handler) {
        return false;
    }
    //调用handler函数
    return (this->*handler)(args, err);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
class Service::OptionHandlerMap : public KeywordMap<OptionHandler> {
    ...........
    Service::OptionHandlerMap::Map& Service::OptionHandlerMap::map() const {
    constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
    static const Map option_handlers = {
        {"class",       {1,     1,    &Service::HandleClass}},
        {"console",     {0,     0,    &Service::HandleConsole}},
        {"critical",    {0,     0,    &Service::HandleCritical}},
        {"disabled",    {0,     0,    &Service::HandleDisabled}},
        {"group",       {1,     NR_SVC_SUPP_GIDS + 1, &Service::HandleGroup}},
        {"ioprio",      {2,     2,    &Service::HandleIoprio}},
        {"keycodes",    {1,     kMax, &Service::HandleKeycodes}},
        {"oneshot",     {0,     0,    &Service::HandleOneshot}},
        {"onrestart",   {1,     kMax, &Service::HandleOnrestart}},
        {"seclabel",    {1,     1,    &Service::HandleSeclabel}},
        {"setenv",      {2,     2,    &Service::HandleSetenv}},
        {"socket",      {3,     6,    &Service::HandleSocket}},
        {"user",        {1,     1,    &Service::HandleUser}},
        {"writepid",    {1,     kMax, &Service::HandleWritepid}},
    };
    return option_handlers;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
//以class对应的处理函数为例,可以看出其实就是填充service对象对应的域
bool Service::HandleClass(const std::vector<std::string>& args, std::string* err) {
    classname_ = args[1];
    return true;
}

//注意此时service对象已经处理完毕
void ServiceParser::EndSection() {
    if (service_) {
        ServiceManager::GetInstance().AddService(std::move(service_));
    }
}

void ServiceManager::AddService(std::unique_ptr service) {
    Service* old_service = FindServiceByName(service->name());
    if (old_service) {
        ERROR("ignored duplicate definition of service '%s'",
              service->name().c_str());
        return;
    }
    //将service对象加入到services_里
    //7.0里,services_已经是个vector了
    services_.emplace_back(std::move(service));
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
總結一下:ServiceParser中,首先根據第一行的名字和參數創建出service對象,然後根據選項域的內容填充service對象,最後將創建出的service對象加入到vector類型的service鍊錶中。
ActionParser 
ActionParser定義於system/core/init/action.cpp中。Action的解析過程,其實與Service一樣,也是先後調用ParseSection, ParseLineSection和EndSection。
bool ActionParser::ParseSection(....) {
    ........
    //创建出新的action对象
    auto action = std::make_unique(false);
    //根据参数,填充action的trigger域,不详细分析了
    if (!action->InitTriggers(triggers, err)) {
        return false;
    }
    .........
}

bool ActionParser::ParseLineSection(.......) const {
    //构造Action对象的command域
    return action_ ? action_->AddCommand(args, filename, line, err) : false;
}

bool Action::AddCommand(.....) {
    ........
    //找出action对应的执行函数
    auto function = function_map_->FindFunction(args[0], args.size() - 1, err);
    ........
    //利用所有信息构造出command,加入到action对象中
    AddCommand(function, args, filename, line);
    return true;
}

void Action::AddCommand(......) {
    commands_.emplace_back(f, args, filename, line);
}

void ActionParser::EndSection() {
    if (action_ && action_->NumCommands() > 0) {
        ActionManager::GetInstance().AddAction(std::move(action_));
    }
}

void ActionManager::AddAction(.....) {
    ........
    auto old_action_it = std::find_if(actions_.begin(),
                     actions_.end(),
                     [&action] (std::unique_ptr& a) {
                         return action->TriggersEqual(*a);
                     });

    if (old_action_it != actions_.end()) {
        (*old_action_it)->CombineAction(*action);
    } else {
        //加入到action链表中,类型也是vector,其中装的是指针
        actions_.emplace_back(std::move(action));
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
可以看出,加載action塊的邏輯和service一樣,不同的是需要填充trigger和command域。當然,最後解析出的action也需要加入到action鍊錶中。
這裡最後還剩下一個問題,那就是哪裡定義了Action中command對應處理函數? 
答案就是在init.cpp的main函數中:
.......
const BuiltinFunctionMap function_map;
Action::set_function_map(&function_map);
.......
  • 1
  • 2
  • 3
  • 4
Action中調用function_map_->FindFunction時,實際上調用的是BuiltinFunctionMap的FindFunction函數。FindFunction是keyword定義的通用函數,重點是重構的map函數。所以需要看BuiltinFunctionMap,其定義在system/core/init/builtins.cpp:
BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
    constexpr std::size_t kMax = std::numeric_limits::max();
    static const Map builtin_functions = {
        {"bootchart_init",          {0,     0,    do_bootchart_init}},
        {"chmod",                   {2,     2,    do_chmod}},
        {"chown",                   {2,     3,    do_chown}},
        {"class_reset",             {1,     1,    do_class_reset}},
        {"class_start",             {1,     1,    do_class_start}},
        {"class_stop",              {1,     1,    do_class_stop}},
        {"copy",                    {2,     2,    do_copy}},
        {"domainname",              {1,     1,    do_domainname}},
        {"enable",                  {1,     1,    do_enable}},
        {"exec",                    {1,     kMax, do_exec}},
        {"export",                  {2,     2,    do_export}},
        {"hostname",                {1,     1,    do_hostname}},
        {"ifup",                    {1,     1,    do_ifup}},
        {"init_user0",              {0,     0,    do_init_user0}},
        {"insmod",                  {1,     kMax, do_insmod}},
        {"installkey",              {1,     1,    do_installkey}},
        {"load_persist_props",      {0,     0,    do_load_persist_props}},
        {"load_system_props",       {0,     0,    do_load_system_props}},
        {"loglevel",                {1,     1,    do_loglevel}},
        {"mkdir",                   {1,     4,    do_mkdir}},
        {"mount_all",               {1,     kMax, do_mount_all}},
        {"mount",                   {3,     kMax, do_mount}},
        {"powerctl",                {1,     1,    do_powerctl}},
        {"restart",                 {1,     1,    do_restart}},
        {"restorecon",              {1,     kMax, do_restorecon}},
        {"restorecon_recursive",    {1,     kMax, do_restorecon_recursive}},
        {"rm",                      {1,     1,    do_rm}},
        {"rmdir",                   {1,     1,    do_rmdir}},
        {"setprop",                 {2,     2,    do_setprop}},
        {"setrlimit",               {3,     3,    do_setrlimit}},
        {"start",                   {1,     1,    do_start}},
        {"stop",                    {1,     1,    do_stop}},
        {"swapon_all",              {1,     1,    do_swapon_all}},
        {"symlink",                 {2,     2,    do_symlink}},
        {"sysclktz",                {1,     1,    do_sysclktz}},
        {"trigger",                 {1,     1,    do_trigger}},
        {"verity_load_state",       {0,     0,    do_verity_load_state}},
        {"verity_update_state",     {0,     0,    do_verity_update_state}},
        {"wait",                    {1,     2,    do_wait}},
        {"write",                   {2,     2,    do_write}},
    };
    return builtin_functions;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
上述代碼的第四項就是Action每個command對應的執行函數。

11.向執行隊列中添加其它action

ActionManager& am = ActionManager::GetInstance();

am.QueueEventTrigger("early-init");

// Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
m.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
// ... so that we can start queuing up actions that require stuff from /dev.
am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
am.QueueBuiltinAction(set_mmap_rnd_bits_action, "set_mmap_rnd_bits");
am.QueueBuiltinAction(keychord_init_action, "keychord_init");
am.QueueBuiltinAction(console_init_action, "console_init");

// Trigger all the boot actions to get us started.
am.QueueEventTrigger("init");

// Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
// wasn't ready immediately after wait_for_coldboot_done
am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");

// 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");
}

// Run all property triggers based on current state of the properties.
    am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
從上面的代碼可以看出,接下來init進程中調用了大量的QueueEventTrigger和QueueBuiltinAction函數。
void ActionManager::QueueEventTrigger(const std::string& trigger) {
    trigger_queue_.push(std::make_unique(trigger));
}
  • 1
  • 2
  • 3
此處QueueEventTrigger函數就是利用參數構造EventTrigger,然後加入到trigger_queue_中。後續init進程處理trigger事件時,將會觸發相應的操作。根據前文的分析,我們知道實際上就是將action_list中,對應trigger與第一個參數匹配的action,加入到運行隊列action_queue中。
void ActionManager::QueueBuiltinAction(BuiltinFunction func, const std::string& name) {
    //创建action
    auto action = std::make_unique(true);
    std::vector<std::string> name_vector{name};

    //保证唯一性
    if (!action->InitSingleTrigger(name)) {
        return;
    }

    //创建action的cmd,指定执行函数和参数
    action->AddCommand(func, name_vector);

    trigger_queue_.push(std::make_unique(action.get()));
    actions_.emplace_back(std::move(action));
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
QueueBuiltinAction函數中構造新的action加入到actions_中,第一個參數作為新建action攜帶cmd的執行函數;第二個參數既作為action的trigger name,也作為action攜帶cmd的參數。

12.處理添加到運行隊列的事件

while (true) {
    //判断是否有事件需要处理
    if (!waiting_for_exec) {
        //依次执行每个action中携带command对应的执行函数
        am.ExecuteOneCommand();
        //重启一些挂掉的进程
        restart_processes();
    }

    //以下决定timeout的时间,将影响while循环的间隔
    int timeout = -1;
    //有进程需要重启时,等待该进程重启
    if (process_needs_restart) {
        timeout = (process_needs_restart - gettime()) * 1000;
        if (timeout < 0)
            timeout = 0;
    }

    //有action待处理,不等待
    if (am.HasMoreCommands()) {
        timeout = 0;
    }

    //bootchart_sample应该是进行性能数据采样
    bootchart_sample(&timeout);

    epoll_event ev;
    //没有事件到来的话,最多阻塞timeout时间
    int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));
    if (nr == -1) {
        ERROR("epoll_wait failed: %s\n", strerror(errno));
    } else if (nr == 1) {
        //有事件到来,执行对应处理函数
        //epoll句柄(即epoll_fd)主要监听子进程结束,及其它进程设置系统属性的请求。
        ((void (*)()) ev.data.ptr)();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
init進程將所有需要操作的action加入運行隊列後, 進入無限循環過程,不斷處理運行隊列中的事件,同時進行重啟service等操作。
ExecuteOneCommand中的主要部分如下所示
void ActionManager::ExecuteOneCommand() {
    // Loop through the trigger queue until we have an action to execute
    //当有可执行action或trigger queue为空时结束
    while (current_executing_actions_.empty() && !trigger_queue_.empty()) {
        //轮询actions链表
        for (const auto& action : actions_) {
            //依次查找trigger表
            if (trigger_queue_.front()->CheckTriggers(*action)) {
                //当action与trigger对应时,就可以执行当前action
                //一个trigger可以对应多个action,均加入current_executing_actions_
                current_executing_actions_.emplace(action.get());
            }
        }
        //trigger event出队
        trigger_queue_.pop();
    }

    if (current_executing_actions_.empty()) {
        return;
    }

    //每次只执行一个action,下次init进程while循环时,跳过上面的while循环,接着执行
    auto action = current_executing_actions_.front();

    if (current_command_ == 0) {
        std::string trigger_name = action->BuildTriggersString();
        INFO("processing action (%s)\n", trigger_name.c_str());
    }

    //实际的执行过程,此处仅处理当前action中的一个cmd
    action->ExecuteOneCommand(current_command_);

    //适当地清理工作,注意只有当前action中所有的command均执行完毕后,才会将该action从current_executing_actions_移除
    // If this was the last command in the current action, then remove
    // the action from the executing list.
    // If this action was oneshot, then also remove it from actions_.
    ++current_command_;
    if (current_command_ == action->NumCommands()) {
        current_executing_actions_.pop();
        current_command_ = 0;
        if (action->oneshot()) {
            auto eraser = [&action] (std::unique_ptr& a) {
                return a.get() == action;
            };
            actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser));
        }
    }
}

void Action::ExecuteCommand(const Command& command) const {
    Timer t;
    //执行该command对应的处理函数
    int result = command.InvokeFunc();
    ........
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
void Action::ExecuteCommand(const Command& command) const {
    Timer t;
    //执行该command对应的处理函数
    int result = command.InvokeFunc();
    ........
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
從代碼可以看出,當while循環不斷調用ExecuteOneCommand函數時,將按照trigger表的順序,依次取出action鍊錶中與trigger匹配的action。 
每次均執行一個action中的一個command對應函數(一個action可能攜帶多個command)。 
當一個action所有的command均執行完畢後,再執行下一個action。 
當一個trigger對應的action均執行完畢後,再執行下一個trigger對應action。
restart_processes函數負責按需重啟service
static void restart_processes() {
    process_needs_restart = 0;
    ServiceManager::GetInstance().ForEachServiceWithFlags(
        SVC_RESTARTING,
        [] (Service* s) {
            s->RestartIfNeeded(process_needs_restart);
        });
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
該函數輪詢service對應的鍊錶,對於有SVC_RESTARING標誌的service執行RestartIfNeeded(當子進程終止時,init進程會將可被重啟進程的服務標誌位置為SVC_RESTARTING)。
RestartIfNeeded可以重新啟動服務。
void Service::RestartIfNeeded(time_t& process_needs_restart)(struct service *svc)
{
    time_t next_start_time = svc->time_started + 5;

    //两次服务启动时间的间隔要大于5s
    if (next_start_time <= gettime()) {
        svc->flags &= (~SVC_RESTARTING);
        //满足时间间隔的要求后,重启服务
        //Start将会重新fork服务进程,并做相应的配置
        Start(svc, NULL);
        return;
    }

    //更新main函数中,while循环需要等待的时间
    if ((next_start_time < process_needs_restart) ||
        (process_needs_restart == 0)) {
        process_needs_restart = next_start_time;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
Bootchart 是一個能對GNU/Linux boot 過程進行性能分析並把結果直觀化的工具。它在boot 過程中蒐集資源利用情況及進程信息然後以PNG, SVG或EPS格式來顯示結果。BootChart 包含數據收集工具和圖像產生工具。數據收集工具在原始的BootChart中是獨立的shell程序,但在Android中,數據收集工具被集成到了init 程序中。

Android 中的Dalvik和ART是什么,有啥区别?

原文 https://www.jianshu.com/p/58f817d176b7

什麼是Dalvik?


Dalvik是Google公司自己設計用於Android平台的虛擬機。
Dalvik虛擬機是Google等廠商合作開發的Android移動設備平台的核心組成部分之一。
它可以支持已轉換為** .dex格式**的Java應用程序的運行,.dex格式是專為Dalvik設計的一種壓縮格式,適合內存和處理器速度有限的系統。
Dalvik經過優化,允許在有限的內存中同時運行多個虛擬機的實例,並且每一個Dalvik應用作為一個獨立的Linux進程執行。獨立的進程可以防止在虛擬機崩潰的時候所有程序都被關閉。
很長時間以來,Dalvik虛擬機一直被用戶指責為拖慢安卓系統運行速度不如IOS的根源。
2014年6月25日,Android L正式亮相於召開的谷歌I/O大會,Android L改動幅度較大,谷歌將直接刪除Dalvik,代替它的是傳聞已久的ART。

Dalvik和JVM有啥關係?


主要區別:
Dalvik是基於寄存器的,而JVM是基於棧的。
Dalvik運行dex文件,而JVM運行java字節碼
自Android 2.2開始,Dalvik支持JIT(just-in-time,即時編譯技術)。
優化後的Dalvik較其他標準虛擬機存在一些不同特性 : 
1.佔用更少空間 
2.為簡化翻譯,常量池只使用32位索引  
3.標準Java字節碼實行8位堆棧指令,Dalvik使用16位指令集直接作用於局部變量。局部變量通常來自4位的“虛擬寄存器”區。這樣減少了Dalvik的指令計數,提高了翻譯速度。 
 當Android啟動時,Dalvik VM監視所有的程序(APK),並且創建依存關係樹,為每個程序優化代碼並存儲在Dalvik緩存中。Dalvik第一次加載後會生成Cache文件,以提供下次快速加載,所以第一次會很慢。
 Dalvik解釋器採用預先算好的Goto地址,每個指令對內存的訪問都在64字節邊界上對齊。這樣可以節省一個指令後進行查表的時間。為了強化功能, Dalvik還提供了快速翻譯器(Fast Interpreter)。
一般來說,基於堆棧的機器必須使用指令才能從堆棧上的加載和操作數據,因此,相對基於寄存器的機器,它們需要更多的指令才能實現相同的性能。但是基於寄存器機器上的指令必須經過編碼,因此,它們的指令往往更大。
Dalvik虛擬機既不支持Java SE 也不支持Java ME類庫(如:Java類,AWT和Swing都不支持)。相反,它使用自己建立的類庫(Apache Harmony Java的一個子集)。

什麼是ART?


即Android Runtime
ART的機制與Dalvik不同。在Dalvik下,應用每次運行的時候,字節碼都需要通過即時編譯器(just in time ,JIT)轉換為機器碼,這會拖慢應用的運行效率,而在ART環境中,應用在第一次安裝的時候,字節碼就會預先編譯成機器碼,使其成為真正的本地應用。這個過程叫做預編譯(AOT,Ahead-Of-Time)。這樣的話,應用的啟動(首次)和執行都會變得更加快速。
ART有什麼優缺點呢?

優點:
1、系統性能的顯著提升。
2、應用啟動更快、運行更快、體驗更流暢、觸感反饋更及時。
3、更長的電池續航能力。
4、支持更低的硬件。
缺點:
1.機器碼佔用的存儲空間更大,字節碼變為機器碼之後,可能會增加10%-20%(不過在應用包中,可執行的代碼常常只是一部分。比如最新的Google+ APK是28.3 MB,但是代碼只有6.9 MB。)
2.應用的安裝時間會變長。
tips:現在智能手機大部分都可以讓用戶選擇使用Dalvik還是ART模式。當然默認還是使用Dalvik模式。
用法:設置-輔助功能-開發者選項(開發人員工具)-選擇運行環境(不同的手機設置的步驟可能不一樣)。


作者:姜康
链接:https://www.jianshu.com/p/58f817d176b7
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

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系統的啟動流程。