前言

做过接近两年的android,特整理文档。第一篇,先了解android系统的启动流程。主要讲的是从init进程开始。主要讲的是基于Android M的开机启动流程介绍,当然也会分析下Android N版本的启动流程。

Android 系统的平台架构

android intent启动一个 android启动流程分析_android intent启动一个

Android 系统的底层是建立在Linux系统之上,该平台是由应用层(System apps),应用框架层(framework),系统运行库层(C/C++程序库和Android运行时库ART),硬件抽象层(HAL),Linux 内核层构成。

做应用开发的同学,手上肯定有一本书叫做 《android疯狂讲义》,在这本书的一开始就贴出了这张android系统的体系架构图。每一歌部分的功能不再做介绍,有兴趣的同学可以自行查询。

而android的启动流程大体分为三个阶段,1 bootloader引导 2 启动Kernel 3 启动Android 细分如下图所示

android intent启动一个 android启动流程分析_Android_02

第一步:启动电源以及系统启动

    当电源按下,引导芯片代码开始从预定义的地方(固化在ROM)开始执行。加载引导程序到RAM,然后执行。

第二步:系统引导

    相关代码在(bootable\bootloader)

    加电后,CPU先执行bootloader程序,正常启动系统,加载boot.img,boot.img中包含内核。

第三步 :内核kernel

    由bootloader加载kernel,kernel经自解压、初始化、载入built-in 驱动程序完成启动。Kernel启动后会创建若干内核线程(kernel thread),之后装入并执行程序/sbin/init/,载入init process,切换至user-space。
     内核启动时,设置缓存、被保护存储器、计划列表,加载驱动。当内核完成系统设置,它首先在系统文件中寻找”init”文件,然后启动root进程或者系统的第一个进程。

第四步 :Init 进程启动

Android 从linux 系统启动有4个步骤: 1.Init  进程启动 2. Zygote服务启动 3. System server.android服务启动 4. HOME启动

Init进程,是第一个由内核启动的用户级进程。用户自行启动(已经被载入内存,开始运行,并已初始化所有设备驱动程序和数据结构等)之后,就通过启动一个用户级程序init的方式,完成引导进程。Init始终是第一个进程。我们可以说它是root进程或者说有进程的父进程。init进程有两个责任,一是挂载目录,比如/sys、/dev、/proc,二是运行init.rc脚本。
Init 进程启动后,根据Init.rc和init.xxx.rc的脚本文件建立基本服务。

init 进程是在kernel/init/main.c文件中启动的,启动过程如下start_kernel()->rest_init()->kernel_init()->run_init_process(“/init”)

run_init_process函数如下,通过传入文件名字,调用do_execve函数,来启动新的程序的,

static int run_init_process(const char *init_filename)
 {
     argv_init[0] = init_filename;
     return do_execve(init_filename,
         (const char __user *const __user *)argv_init,
         (const char __user *const __user *)envp_init);
 }

在 init.cpp main 函数中,首先做的事情的创建一些文件夹并且挂在设备,代码如下

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);

接下来是三个函数

open_devnull_stdio();
     klog_init()
     klog_set_level(KLOG_NOTICE_LEVEL);
     open_devnull_stdio()是将标准输入,标准输出以及错误输出都重定向到了/dev/_null_的设备节点,而_null_ 是一个无底洞。
     klog_init()  查看源代码的话是 打开了一个/dev/__kmsg__的节点,设定init的输出设备为/dev/__kmsg__
     property_init() 主要是为属性分配一些存储空间.

下面到了我们最重要的init_parse_config_file("/init.rc")函数了。init解析。

这个函数是init.rc文件解析的开始函数,我们看它做了什么事。

int init_parse_config_file(const char* path) {
     read_file(path, &data)       // 将init.rc文件里面的数据读取到data里面
     parse_config(path, data);   // 对data数据进行解析
     dump_parser_state();
 }

下面是parse_config 函数,代码较多

static void parse_config(const char *fn, const std::string& data)
 {
     struct listnode import_list;
     struct listnode *node;
     char *args[INIT_PARSER_MAXARGS];    int nargs = 0;
    parse_state state;
     state.filename = fn;
     state.line = 0;
     state.ptr = strdup(data.c_str());  // c_str():生成一个const char*指针,指向以空字符终止的数组。数组的数据是临时的,当有一个改变这些数据的成员函数被调用后,其中的数据就会失效。因此要么现用先转换,要么把它的数据复制到用户自己可以管理的内存中。
     state.nexttoken = 0;
     state.parse_line = parse_line_no_op;    list_init(&import_list);
     state.priv = &import_list;/*  开始获取每一个token,然后分析这些token,每一个token就是有空格、字表符和回车符分隔的字符串 */    for (;;) {
         switch (next_token(&state)) { //查看next_token函数源代码的话,next_token函数相当于词法分析器
         case T_EOF:                          // init.rc 文件分析完毕
             state.parse_line(&state, 0, 0);
             goto parser_done;
         case T_NEWLINE:                 // 分析每一行命令
             state.line++;
             if (nargs) {
                 int kw = lookup_keyword(args[0]);   // 这个函数就有意思了,通过对每一行第一个单词进行匹配
                 if (kw_is(kw, SECTION)) {
                     state.parse_line(&state, 0, 0);
                     parse_new_section(&state, kw, nargs, args); 
                 } else {
                     state.parse_line(&state, nargs, args);
                 }
                 nargs = 0;
             }
             break;
         case T_TEXT:
             if (nargs < INIT_PARSER_MAXARGS) {
                 args[nargs++] = state.text;
             }
             break;
         }
     }parser_done:
     list_for_each(node, &import_list) {
          struct import *import = node_to_item(node, struct import, list);
          int ret;         ret = init_parse_config_file(import->filename);
          if (ret)
              ERROR("could not import file '%s' from '%s'\n",
                    import->filename, fn);
     }
 }int init_parse_config_file(const char* path) {
     INFO("Parsing %s...\n", path);
     Timer t;
     std::string data;
     if (!read_file(path, &data)) {
         return -1;
     }    data.push_back('\n'); // TODO: fix parse_config.
     parse_config(path, data);
     dump_parser_state();    NOTICE("(Parsing %s took %.2fs.)\n", path, t.duration());
     return 0;
 }

以下是对与每一行语句进行分析,分类存储

static void parse_new_section(struct parse_state *state, int kw,
                        int nargs, char **args)
 {
     switch(kw) {
     case K_service:
         state->context = parse_service(state, nargs, args);       //如果标志是K-service 调用parse_service()中的 list_add_tail(&service_list, &svc->slist);将数据存储在service_list。
       //注意此时parse_service中的svc对象基本上是一个空壳,因为相关的options还没有解析
         if (state->context) {
             state->parse_line = parse_line_service;//解析service对应的options行,主要是填充parse_service()中创建的service对象。
             return;
         }
         break;
     case K_on:
         state->context = parse_action(state, nargs, args); //同理,如果标志位是K_on, 加到action_list上
         if (state->context) {
             state->parse_line = parse_line_action;
             return;
         }
         break;
     case K_import:
         parse_import(state, nargs, args);同理,如果标志位是K_import, 加到import_list上
         break;
     }
     state->parse_line = parse_line_no_op;
 }

看下init.rc 文件就明白了

 

import /init.trace.rc
on early-init
     # Set init and its forked children's oom_adj.
     write /proc/1/oom_score_adj -1000    # Set the security context of /adb_keys if present.
     restorecon /adb_keys    start ueventd
service ueventd /sbin/ueventd
     class core
     critical
     seclabel u:r:ueventd:s0

以上过程就是对init.rc文件存储的数据进行解析的。

action_list存放了所有探知的action

service_list存放了所有在init.rc文件中定义的 服务。

 

queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");

这个函数 维护action_queue,存放了 将要执行的 action。