现在的位置: 首页 > 综合 > 正文

Android 源码分析 — (一) Android启动过程

2013年09月16日 ⁄ 综合 ⁄ 共 6447字 ⁄ 字号 评论关闭

将创建的PDF文件导入到cnBlogs中排版实在是不太方便,所以直接分享到slideshare上了。

简单分析了一下Android启动过程,错漏之处敬请指正。

 
复制代码
Android 源码分析 -- (一) Android启动过程
1. 源码文件路径: platform/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;
    if (!strcmp(basename(argv[0]), "ueventd"))
        2) 基于C语言的风格,在函数入口处声明一些后续会使用的变量。
        0) 这个代码文件主要用于实现Android系统中init进程 (init进程为Android系统中用户空间启动的第一个进程,其作用相当于Linux系统中的init进程)
      NOTE: 如果调用此文件生成的可执行文件的第一个参数为“ueventd”,那么此文件将实现Android系统中的 “ueventd” 进程。
      1) 在进行编译后,此文件生成的可执行程序名称为”init”,同时会生成一个指向此文件的软链接: “ueventd”
      3) 如果执行此文件生成的可执行程序的方式类似于: “ueventd xxx”。也即是基于执行此文件对应的软链接: /sbin/ueventd时会调用”ueventd_main”函数,进而生成”ueventd”       进程。
      4) Android系统中的 ueventd 进程用于实现用户态进程与内核进行数据通信。
      5) 在Android系统的init.rc文件: platform/system/core/rootdir/init.rc中通过如下方式来启动 ueventd 进程:
      on early-init
    # Set init and its forked children's oom_adj.
        write /proc/1/oom_adj -16
        start ueventd
        return ueventd_main(argc, argv);
    /* clear the umask */
    umask(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.
    */
    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);
    /* indicate that booting is in progress to background fw loaders, etc */
    close(open("/dev/.booting", O_WRONLY | O_CREAT, 0000));
    /* 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();
    INFO("reading config file\n");
    6) 生成Android系统中的一些基本的系统目录并挂载对应的文件系统。
        7) 生成 “/dev/__null__” 虚拟设备(类似于Linux系统中的 /dev/null 设备)并将stdin/stdout/stderr三个文件重定向到 “/dev/__null__”
        8) 生成 ” /dev/__kmsg__” 虚拟设备用于记录log。
        Klog_init 实现文件: system/core//libcutils/klog.c
        9) 解析 init.rc (platform/system/core/rootdir/init.rc)。
        init_parse_config_file("/init.rc");
    /* pull the kernel commandline and ramdisk properties file in */
    import_kernel_cmdline(0, import_kernel_nv);
    /* don't expose the raw commandline to nonpriv processes */
    chmod("/proc/cmdline", 0440);
    get_hardware_name(hardware, &revision);
    snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware);
    init_parse_config_file(tmp);
    action_for_each_trigger("early-init", action_add_queue_tail);
    queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
    queue_builtin_action(property_init_action, "property_init");
    queue_builtin_action(keychord_init_action, "keychord_init");
    queue_builtin_action(console_init_action, "console_init");
    queue_builtin_action(set_init_properties_action, "set_init_properties");
    /* execute all the boot actions to get us started */
    action_for_each_trigger("init", action_add_queue_tail);
    10) 从 “/proc/cmdline” 中读取内核命令行参数,
      对应函数实现路径: platform/system/core/init/util.c
          11) 在第 10 步读取完 /proc/cmdline 中的参数后,修改此文件的权限,禁止非授权用户操作此文件。
          12) 从 “/proc/cpuinfo” 中读取系统的CPU硬件信息。
        对应函数实现路径: platform/system/core/init/util.c
          13) 基于第12步中读取的硬件信息来解析特定于硬件的配置信息。
          14) 基于”early-init”,”property_init”,”keychord_init”,”console_init”标识,使用” queue_builtin_action”来过滤上述操作中解析的init.rc文件中的action        并将符合条件的action添加到对应的 action_queue 中,然后调用” action_for_each_trigger”来运行这些actions(实际上是action在list中的分类移动操作)。
        对应函数实现路径: platform/system/core/init/init_parser.c
          基于下面的运行逻辑可以看出,运行”init.rc”中的action的顺序为:“early-init” -> “init” -> “early-boot” -> “boot”
        /* skip mounting filesystems in charger mode */
        if (strcmp(bootmode, "charger") != 0)
        {
            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 (!strcmp(bootmode, "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_propety_triggers");
#if BOOTCHART
        queue_builtin_action(bootchart_init_action, "bootchart_init");
#endif
        for (;;)
        {
            int nr, i, timeout = -1;
            execute_one_command();
            restart_processes();
            15) “init” 进程开始进行”循环事件处理”逻辑。
                16) 运行第14步操作中所分类整理的action_queue中对应的action。
                17) 查看当前的服务进程状态,如果有服务进程退出,重启对应服务进程。
                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;
                }
                18) 基于 property_service 的事件句柄填充 poll event 结构体,用于后续poll操作。
                    22) 如果当前的action_queue 中有需要处理的action,那么下面调用poll时的timeout设置为0,这样就不会因为poll在无事件激发时而阻塞导致当前的init进程            对action处理的的延迟,从而提高 init 进程对action处理的实时性。
                    19) 处理当前init 进程上接收到的signal,主要是处理SIGCHLD。
                    20) 基于 keychord service 的事件句柄填充 poll event 结构体,用于后续poll操作。
                    21) 如果有所监控的子进程退出,此时init进程需要负责重新启动这些退出的服务进程,因此下面调用poll时的timeout设置为0,这样就不会因为poll在无事件激发              时而阻塞导致当前的init进程对”重启服务进程”处理的的延迟,从而可以尽快恢复退出的服务进程。
                    if (!action_queue_empty() || cur_action)
                        timeout = 0;
#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())
                            23) 关于” BOOTCHART”参数的解释:
                        /*
                        * 如果在编译选项中添加了BOOTCHART 参数,那么意味着在系统的启动
                        * 过程中需要生成bootchart(http://www.bootchart.org/),用于后续
                        * Android 启动过程中的性能分析并生成系统启动过程的可视图表。
                        * makefile中的编译选项如下:
                        ifeq ($(strip $(INIT_BOOTCHART)),true)
                        LOCAL_SRC_FILES += bootchart.c
                        LOCAL_CFLAGS += -DBOOTCHART=1
                        endif
                        bootchart的实现文件: platform/system/core/init/bootchart.c
                        */
                        24) 类似于网络服务器开发中常见的基于”poll”机制来检测所关注的句柄上是否有指定的事件激发。
                            25) 如果当前所关注的事件句柄上有事件发生,进行对应的事件处理。
                            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;
}
复制代码

抱歉!评论已关闭.