Android6.0系统启动流程分析

Android6.0系统启动流程分析一:init进程
阳光玻璃杯
https://blog.csdn.net/u011913612/article/details/53204253
到了Android6.0,Init进程使用c++来写了,不过没有关系,它和c写的init没有太大的区别。
Init进程的入口代码是:system\core\init\init.cpp
main函数:
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.
    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);
        mount("proc", "/proc", "proc", 0, NULL);
        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.
    open_devnull_stdio();
    klog_init();
    klog_set_level(KLOG_NOTICE_LEVEL);
    NOTICE("init%s started!\n", is_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));
        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();
        // Propogate 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.
    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) {
        if (restorecon("/init") == -1) {
            ERROR("restorecon failed: %s\n", strerror(errno));
            security_failure();
        }
        char* path = argv[0];
        char* args[] = { path, const_cast("--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.
    INFO("Running restorecon...\n");
    restorecon("/dev");
    restorecon("/dev/socket");
    restorecon("/dev/__properties__");
    restorecon_recursive("/sys");
    epoll_fd = epoll_create1(EPOLL_CLOEXEC);
    if (epoll_fd == -1) {
        ERROR("epoll_create1 failed: %s\n", strerror(errno));
        exit(1);
    }
    signal_handler_init();
    property_load_boot_defaults();
    start_property_service();
    init_parse_config_file("/init.rc");
    action_for_each_trigger("early-init", action_add_queue_tail);
    // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
    queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
    // ... so that we can start queuing up actions that require stuff from /dev.
    queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
    queue_builtin_action(keychord_init_action, "keychord_init");
    queue_builtin_action(console_init_action, "console_init");
    // Trigger all the boot actions to get us started.
    action_for_each_trigger("init", action_add_queue_tail);
    // Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
    // wasn't ready immediately after wait_for_coldboot_done
    queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
    // Don't mount filesystems or start core system services in charger mode.
    char bootmode[PROP_VALUE_MAX];
    if (property_get("ro.bootmode", bootmode) > 0 && strcmp(bootmode, "charger") == 0) {
        action_for_each_trigger("charger", action_add_queue_tail);
    } else {
        action_for_each_trigger("late-init", action_add_queue_tail);
    }
    // Run all property triggers based on current state of the properties.
    queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");
    while (true) {
        if (!waiting_for_exec) {
            execute_one_command();
            restart_processes();
        }
        int timeout = -1;
        if (process_needs_restart) {
            timeout = (process_needs_restart - gettime()) * 1000;
            if (timeout < 0)
                timeout = 0;
        }
        if (!action_queue_empty() || cur_action) {
            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.这个函数是否往下执行取决于传入的参数,如果第0个参数的basename为ueventd,则执行ueventd_main(argc, argv);如果basename为watchdogd_main,则执行watchdogd_main(argc, argv);只有basename不为这二者时,才会继续往下执行。
2.如果argv[1]不为”–second-stage”或者只有一个参数的话,那么is_first_stage就为true,就会创建/dev/pts和”/dev/socket”两个设备文件节点,并挂载一个文件系统。可以看出来init进程分两个阶段,不同的阶段有不同的行为。具体的内涵鄙人还没搞明白。
3.启动属性服务。创建一个socket,并在之后的死循环中监听这个socket返回的文件描述符。
3.解析init.rc。这个过程也是我最感兴趣的,也是最重要的复杂的。
4.对各个阶段的action排序。
5.进入死循环。
6.第一次进入死循环后,action_queue里面有很多时间,因此需要不断调用execute_one_command来执行命令。此时,action_queue_empty为假,timeout 为0,init线程不会在epoll_wait方法中休眠,因为设置的timeout=0哦,这一点曾一度困扰了我。
7.所有的命令执行完后,init进程进入休眠,监听property_set_fd和signal_read_fd两个文件描述符,一点他们有事件过来,立刻被唤醒,进而做事件处理。
init.rc梳理
在我们分析init.rc的解析过程之前,我们还需要先对init.rc有个基本的认识。
先看一张我根据理解绘制的图:
Android6.0系统启动流程分析_第1张图片
从图来看,init.rc主要有section组成,section由on,import,section三个关键字标示。其中on标示的section叫做action。
import就不用说了,和c语言中的include功能有点类似。
service格式如下
service [ ]* 
  

你可能感兴趣的:(Android)