Android系统启动流程分析init.c和init.rc文件

init是用户空间执行的第一个程序。其作用如下:

1:建立设备驱动文件、(位于/dev目录中)、内存文件(/proc、/sys目录等。

2:初始化属性

3:处理配置文件的命令(主要是init.rc文件),包括处理各种Action

4:性能分析(使用bootchart工具)。

5:无限循环执行command(启动其他的进程)

首先贴出源代码:/system/core/init   init.c文件

int main(int argc, char **argv)
{
    int fd_count = 0;
    struct pollfd ufds[4];
    char *tmpdev;
    char* debuggable;
    char tmp[32];
    int property_set_fd_init = 0;
    int signal_fd_init = 0;
    int keychord_fd_init = 0;
    bool is_charger = false;
    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);
    //  下面的代码开始建立各种用户空间的目录,如/dev、/proc、/sys等
    mkdir("/dev", 0755);
    mkdir("/proc", 0755);
    mkdir("/sys", 0755);
    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);
        /* 检测/dev/.booting文件是否可读写和创建*/
    close(open("/dev/.booting", O_WRONLY | O_CREAT, 0000));
    open_devnull_stdio();
    klog_init();
    //  初始化属性
    property_init();
    get_hardware_name(hardware, &revision);
    //  处理内核命令行
    process_kernel_cmdline();
    … …
    is_charger = !strcmp(bootmode, "charger");
    INFO("property init\n");
    if (!is_charger)
        property_load_boot_defaults();
    INFO("reading config file\n");
    //  分析/init.rc文件的内容
    init_parse_config_file("/init.rc");
    … …//  执行初始化文件中的动作
    action_for_each_trigger("init", action_add_queue_tail);
    //  在charger模式下略过mount文件系统的工作
    if (!is_charger) {
        action_for_each_trigger("early-fs", action_add_queue_tail);
        action_for_each_trigger("fs", action_add_queue_tail);
        action_for_each_trigger("post-fs", action_add_queue_tail);
        action_for_each_trigger("post-fs-data", action_add_queue_tail);
    }
    queue_builtin_action(property_service_init_action, "property_service_init");
    queue_builtin_action(signal_init_action, "signal_init");
    queue_builtin_action(check_startup_action, "check_startup");
    if (is_charger) {
        action_for_each_trigger("charger", action_add_queue_tail);
    } else {
        action_for_each_trigger("early-boot", action_add_queue_tail);
        action_for_each_trigger("boot", 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");
#if BOOTCHART
    queue_builtin_action(bootchart_init_action, "bootchart_init");
#endif
    //  进入无限循环,建立init的子进程(init是所有进程的父进程)
    for(;;) {
        int nr, i, timeout = -1;
        //  执行命令(子进程对应的命令)
        execute_one_command();
        restart_processes();
        if (!property_set_fd_init && get_property_set_fd() > 0) {
            ufds[fd_count].fd = get_property_set_fd();
            ufds[fd_count].events = POLLIN;
            ufds[fd_count].revents = 0;
            fd_count++;
            property_set_fd_init = 1;
        }
        if (!signal_fd_init && get_signal_fd() > 0) {
            ufds[fd_count].fd = get_signal_fd();
            ufds[fd_count].events = POLLIN;
            ufds[fd_count].revents = 0;
            fd_count++;
            signal_fd_init = 1;
        }
        if (!keychord_fd_init && get_keychord_fd() > 0) {
            ufds[fd_count].fd = get_keychord_fd();
            ufds[fd_count].events = POLLIN;
            ufds[fd_count].revents = 0;
            fd_count++;
            keychord_fd_init = 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是一个性能统计工具,用于搜集硬件和系统的信息,并将其写入磁盘,以便其
//  他程序使用
#if BOOTCHART
        if (bootchart_count > 0) {
            if (timeout < 0 || timeout > BOOTCHART_POLLING_MS)
                timeout = BOOTCHART_POLLING_MS;
            if (bootchart_step() < 0 || --bootchart_count == 0) {
                bootchart_finish();
                bootchart_count = 0;
            }
        }
#endif
        //  等待下一个命令的提交
        nr = poll(ufds, fd_count, timeout);
        if (nr <= 0)
            continue;
        for (i = 0; i < fd_count; i++) {
            if (ufds[i].revents == POLLIN) {
                if (ufds[i].fd == get_property_set_fd())
                    handle_property_set_fd();
                else if (ufds[i].fd == get_keychord_fd())
                    handle_keychord();
                else if (ufds[i].fd == get_signal_fd())
                    handle_signal();
            }
        }
    }
    return 0;
}

父进程作用分析如下:

1.  初始化(包括建立/dev、/proc等目录、初始化属性、执行init.rc等初始化文件中的action等)。

2.  使用for循环无限循环建立子进程。

第一项工作很好理解。而第二项工作是init中的核心。在Linux系统中init是一切应用空间进程的父进程。所以我们平常在Linux终端执行的命令,并建立进程。实际上都是在这个无限的for循环中完成的。也就是说,在Linux终端执行ps命令后,看到的所有除了init外的其他进程,都是由init负责创建的。而且init也会常驻内容。当然,如果init挂了,Linux系统基本上就崩溃了。

对于main函数最开始完成的建立目录的工作比较简单,这部分也没什么可以分析的。就是调用了一些普通的API(mkdir)建立一些目录。现在说一些题外话,由于Android的底层源代码(包括init)实际上是属于Linux应用编程领域,所以要想充分理解Android源代码,除了Linux的基本结构要了解外,Linux应用层的API需要熟悉。可参考Unix环境高级编程书籍进行学习应用层函数的调用。

想彻底修改init.rc文件内容的唯一方式是修改Android的ROM中的内核镜像(boot.img)。其实boot.img名曰内核镜像,不过该文件除了包含完整的Linux内核文件(zImage)外,还包括另外一个镜像文件(ramdisk.img)。ramdisk.img就包含了init.rc文件和init命令。所以只有修改ramdisk.img文件中的init.rc文件,并且重新打包boot.img文件,并刷机,才能彻底修改init.rc文件。如果读者有Android源代码,编译后,就会看到out目录中的相关子目录会生成一个root目录,该目录实际上就是ramdisk.img解压后的内容。会看到有init命令和init.rc文件。

你可能感兴趣的:(Android)