========================================================          ========================================================

=              【原创文章】:参考部分博客内容,学习之余进行了大量的筛减细化分析                        =          =                          【特殊申明】:避讳抄袭侵权之嫌疑,特此说明,欢迎转载!                           =   
========================================================          ========================================================

【前言】

  Android启动篇 — init原理(一)中讲解分init进程分析init创建系统目录并挂在相应系统文件、初始化属性域、设置系统属性、启动配置属性服务端等一系列复杂工作,很多工作和知识点跟Linux关系很大,所以没有作过多介绍,而本此对于init.rc的解析则是重中之重,所以单独拿出来进行详细分析。

int main(int argc, char** argv) {
    /* 01. 创建文件系统目录并挂载相关的文件系统 */
    /* 02. 屏蔽标准的输入输出/初始化内核log系统 */
    /* 03. 初始化属性域 */
    /* 04. 完成SELinux相关工作 */•
    /* 05. 重新设置属性 */
    /* 06. 创建epoll句柄 */
    /* 07. 装载子进程信号处理器 */
    /* 08. 设置默认系统属性 */
    /* 09. 启动配置属性的服务端 */
    /* 10. 匹配命令和函数之间的对应关系 */-------------------------------------------------------------------------------------------   // Android启动篇 — init原理(一)中讲解    /* 11. 解析init.rc */
    Parser& parser = Parser::GetInstance();       // 构造解析文件用的parser对象
    // 增加ServiceParser为一个section,对应name为service
    parser.AddSectionParser("service",std::make_unique<ServiceParser>());
    // 增加ActionParser为一个section,对应name为action
    parser.AddSectionParser("on", std::make_unique<ActionParser>());
    // 增加ImportParser为一个section,对应name为service
    parser.AddSectionParser("import", std::make_unique<ImportParser>());
    parser.ParseConfig("/init.rc");      // 开始实际的解析过程

【正文】

  init.rc是一个配置文件,内部由Android初始化语言编写(Android Init Language)编写的脚本,主要包含五种类型语句:Action、Command、Service、Option和Import,在分析代码的过程中我们会详细介绍。

  init.rc的配置代码在:system/core/rootdir/init.rc 中

  init.rc文件是在init进程启动后执行的启动脚本,文件中记录着init进程需执行的操作。

  init.rc文件大致分为两大部分,一部分是以“on”关键字开头的动作列表(action list):

on early-init      // Action类型语句
    # Set init and its forked children's oom_adj.     // #:注释符号
    write /proc//oom_score_adj -
    ... ...
    start ueventd

  Action类型语句格式:

on <trigger> [&& <trigger>]*     // 设置触发器
   <command>
   <command>      // 动作触发之后要执行的命令

  另一部分是以“service”关键字开头的服务列表(service list):  如 Zygote

service ueventd /sbin/ueventd
    class core
    critical
    seclabel u:r:ueventd:s0

  Service类型语句格式:

service <name> <pathname> [ <argument> ]*   // <service的名字><执行程序路径><传递参数>
   <option>       // option是service的修饰词,影响什么时候、如何启动services
   <option>
   ...

  借助系统环境变量或Linux命令,动作列表用于创建所需目录,以及为某些特定文件指定权限,而服务列表用来记录init进程需要启动的一些子进程。如上面代码所示,service关键字后的第一个字符串表示服务(子进程)的名称,第二个字符串表示服务的执行路径。

  值得一提的是在Android 7.0中对init.rc文件进行了拆分,每个服务一个rc文件。我们要分析的zygote服务的启动脚本则在init.zygoteXX.rc中定义。

  在init.rc的import段我们看到如下代码:

import /init.${ro.zygote}.rc     // 可以看出init.rc不再直接引入一个固定的文件,而是根据属性ro.zygote的内容来引入不同的文件

  说明:

  从android5.0开始,android开始支持64位的编译,zygote本身也就有了32位和64位的区别,所以在这里用ro.zygote属性来控制启动不同版本的zygote进程。

  init.rc位于/system/core/rootdir下。在这个路径下还包括四个关于zygote的rc文件。分别是Init.zygote32.rc,Init.zygote32_64.rc,Init.zygote64.rc,Init.zygote64_32.rc,由硬件决定调用哪个文件。

  这里拿32位处理器为例,init.zygote32.rc的代码如下所示:

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    class main         # class是一个option,指定zygote服务的类型为main
    socket zygote stream  root system          # socket关键字表示一个option,创建一个名为dev/socket/zygote,类型为stream,权限为660的socket
    onrestart write /sys/android_power/request_state wake          # onrestart是一个option,说明在zygote重启时需要执行的command
    onrestart write /sys/power/state on
    onrestart restart audioserver
    onrestart restart cameraserver
    onrestart restart media
    onrestart restart netd
    writepid /dev/cpuset/foreground/tasks

  “service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server”

  在Init.zygote32.rc中,定义了一个zygote服务:zygote,由关键字service告诉init进程创建一个名为zygote的进程,这个进程要执行的程序是:/system/bin/app_process,给这个进程四个参数:

    · -Xzygote:该参数将作为虚拟机启动时所需的参数

    · /system/bin:代表虚拟机程序所在目录

    · --zygote:指明以ZygoteInit.java类中的main函数作为虚拟机执行入口

    · --start-system-server:告诉Zygote进程启动SystemServer进程

  接下来,我们回到源码当中,继续分析main函数:

    /* 11. 解析init.rc */
    Parser& parser = Parser::GetInstance();       // 构造解析文件用的parser对象
    // 增加ServiceParser为一个section,对应name为service
    parser.AddSectionParser("service",std::make_unique<ServiceParser>());
    // 增加ActionParser为一个section,对应name为action
    parser.AddSectionParser("on", std::make_unique<ActionParser>());
    // 增加ImportParser为一个section,对应name为service
    parser.AddSectionParser("import", std::make_unique<ImportParser>());
    parser.ParseConfig("/init.rc");      // 开始实际的解析过程

  说明:

  上面在解析init.rc文件时使用了Parser类(在init目录下的init_parser.h中定义), 初始化ServiceParser用来解析 “service”块,ActionParser用来解析"on"块,ImportParser用来解析“import”块,“import”是用来引入一个init配置文件,来扩展当前配置的。

  /system/core/init/readme.txt 中对init文件中的所有关键字做了介绍,主要包含了Actions, Commands, Services, Options, and Imports等,可自行学习解读。

  分析init.rc的解析过程:函数定义于system/core/init/ init_parser.cpp中

bool Parser::ParseConfig(const std::string& path) {
    if (is_dir(path.c_str())) {           // 判断传入参数是否为目录地址
        return ParseConfigDir(path);      // 递归目录,最终还是靠ParseConfigFile来解析实际的文件
    }
    return ParseConfigFile(path);         // 传入传输为文件地址
}

  继续分析ParseConfigFile():

bool Parser::ParseConfigFile(const std::string& path) {
    ... ...
    Timer t;
    std::string data;
    if (!read_file(path.c_str(), &data)) {       // 读取路径指定文件中的内容,保存为字符串形式
        return false;
}
... ...
    ParseData(path, data);        // 解析获取的字符串
    ... ...
}

  跟踪ParseData():

void Parser::ParseData(const std::string& filename, const std::string& data) {
    ... ...
    parse_state state;
    ... ...
    std::vector<std::string> args;

    for (;;) {
        switch (next_token(&state)) {    // next_token以行为单位分割参数传递过来的字符串,最先走到T_TEXT分支
        case T_EOF:
            if (section_parser) {
                section_parser->EndSection();    // 解析结束
            }
            return;
        case T_NEWLINE:
            state.line++;
            if (args.empty()) {
                break;
            }
            // 在前文创建parser时,我们为service,on,import定义了对应的parser
            // 这里就是根据第一个参数,判断是否有对应的parser
            ])) {
                if (section_parser) {
                    // 结束上一个parser的工作,将构造出的对象加入到对应的service_list与action_list中
                    section_parser->EndSection();
                }
                // 获取参数对应的parser
                section_parser = section_parsers_[args[]].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.emplace_back(state.text);     //将本次解析的内容写入到args中
            break;
        }
    }
}

  至此,init.rc解析完,接下来init会执行几个重要的阶段:

int main(int argc, char** argv) {
    /* 01. 创建文件系统目录并挂载相关的文件系统 */
    /* 02. 屏蔽标准的输入输出/初始化内核log系统 */
    /* 03. 初始化属性域 */
    /* 04. 完成SELinux相关工作 */•
    /* 05. 重新设置属性 */
    /* 06. 创建epoll句柄 */
    /* 07. 装载子进程信号处理器 */
    /* 08. 设置默认系统属性 */
    /* 09. 启动配置属性的服务端 */
    /* 10. 匹配命令和函数之间的对应关系 */    /* 11. 解析init.rc*/----------------------------------------------------------------------------  /* 12.  向执行队列中添加其他action */
    // 获取ActionManager对象,需要通过am对命令执行顺序进行控制
    ActionManager& am = ActionManager::GetInstance();
    // init执行命令触发器主要分为early-init,init,late-init,boot等
    am.QueueEventTrigger("early-init");    // 添加触发器early-init,执行on 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(keychord_init_action, "keychord_init");
    am.QueueBuiltinAction(console_init_action, "console_init");

    // Trigger all the boot actions to get us started.
    am.QueueEventTrigger("init");        // 添加触发器init,执行on init内容,主要包括创建/挂在一些目录,以及symlink等

    // 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.
    if (bootmode == "charger") {
    am.QueueEventTrigger("charger");     // on charger阶段
    } ) == ) {
    NOTICE("Booting into ffbm mode\n");
    am.QueueEventTrigger("ffbm");
    } else {
    am.QueueEventTrigger("late-init");          // 非充电模式添加触发器last-init
    }

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

  在last-init最后阶段有如下代码:

# Mount filesystems and start core system services.
on late-init
    trigger early-fs

    # Mount fstab in init.{$device}.rc by mount_all command. Optional parameter
    # '--early' can be specified to skip entries with 'latemount'.
    # /system and /vendor must be mounted by the end of the fs stage,
    # while /data is optional.
    trigger fs
    trigger post-fs

    # Load properties from /system/ + /factory after fs mount. Place
    # this in another action so that the load will be scheduled after the prior
    # issued fs triggers have completed.
    trigger load_system_props_action

    # Mount fstab in init.{$device}.rc by mount_all with '--late' parameter
    # to only mount entries with 'latemount'. This is needed if '--early' is
    # specified in the previous mount_all command on the fs stage.
    # With /system mounted and properties form /system + /factory available,
    # some services can be started.
    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

  可见出发了on early-boot和on boot两个Action。

  我们看一下on boot:

on boot
    # basic network init
    ifup lo
    hostname localhost
    domainname localdomain
    ... ...
    class_start core

  在on boot 的最后class_start core 会启动class为core的服务,这些服务包括ueventd、logd、healthd、adbd(disabled)、lmkd(LowMemoryKiller)、servicemanager、vold、debuggerd、surfaceflinger、bootanim(disabled)等。

  回到主题,分析trigger触发器的代码,QueueEventTrigger():位于system/core/init/action.cpp

void ActionManager::QueueEventTrigger(const std::string& trigger) {
    trigger_queue_.push(std::make_unique<EventTrigger>(trigger));
}

  此处QueueEventTrigger函数就是利用参数构造EventTrigger,然后加入到trigger_queue_中。后续init进程处理trigger事件时,将会触发相应的操作。

  再看一下QueueBuiltinAction()函数:同样位于system/core/init/action.cpp

void ActionManager::QueueBuiltinAction(BuiltinFunction func,
                                   const std::string& name) {
    // 创建action
    auto action = std::make_unique<Action>(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<BuiltinTrigger>(action.get()));
    actions_.emplace_back(std::move(action));
}

  QueueBuiltinAction函数中构造新的action加入到actions_中,第一个参数作为新建action携带cmd的执行函数;第二个参数既作为action的trigger name,也作为action携带cmd的参数。

  接下来继续分析main函数:

int main(int argc, char** argv) {
    /* 01. 创建文件系统目录并挂载相关的文件系统 */
    /* 02. 屏蔽标准的输入输出/初始化内核log系统 */
    /* 03. 初始化属性域 */
    /* 04. 完成SELinux相关工作 */•
    /* 05. 重新设置属性 */
    /* 06. 创建epoll句柄 */
    /* 07. 装载子进程信号处理器 */
    /* 08. 设置默认系统属性 */
    /* 09. 启动配置属性的服务端 */
    /* 10. 匹配命令和函数之间的对应关系 */    /* 11. 解析init.rc*/    /* 12. 向执行队列中添加其他action */-------------------------------------------------------------------    /* 13. 处理添加到运行队列的事件 */    while (true) {
    // 判断是否有事件需要处理
        if (!waiting_for_exec) {
            // 依次执行每个action中携带command对应的执行函数
     am.ExecuteOneCommand();
        // 重启一些挂掉的进程
            restart_processes();
        }

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

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

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

        epoll_event ev;
        // 没有事件到来的话,最多阻塞timeout时间
        , timeout));
        ) {
            ERROR("epoll_wait failed: %s\n", strerror(errno));
        } ) {
            //有事件到来,执行对应处理函数
            //根据上文知道,epoll句柄(即epoll_fd)主要监听子进程结束,及其它进程设置系统属性的请求
            ((void (*)()) ev.data.ptr)();
        }
    }
;
} // end main

  看一下ExecuteOneComand()函数:同样位于system/core/init/action.cpp

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()) {
    // 循环遍历action_队列,包含了所有需要执行的命令,解析init.rc获得
        for (const auto& action : actions_) {
            // 获取队头的trigger, 检查actions_列表中的action的trigger,对比是否相同
            if (trigger_queue_.front()->CheckTriggers(*action)) {
                // 将所有具有同一trigger的action加入当前可执行action队列
                current_executing_actions_.emplace(action.get());
            }
        }
        // 将队头trigger出栈
        trigger_queue_.pop();
    }

    if (current_executing_actions_.empty()) {   // 当前可执行的actions队列为空就返回
        return;
    }

    auto action = current_executing_actions_.front(); // 获取当前可执行actions队列的首个action

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

    action->ExecuteOneCommand(current_command_);     // 执行当前的命令

    // 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_;      // 不断叠加,将action_中的所有命令取出
    if (current_command_ == action->NumCommands()) {
        current_executing_actions_.pop();
        current_command_ = ;
        if (action->oneshot()) {
            auto eraser = [&action] (std::unique_ptr<Action>& a) {
                return a.get() == action;
            };
            actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser));
        }
    }
}

  我们来观察一下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      // 后面我们即将重点分析zygote进程

  通过ro.zygote的属性import对应的zygote的rc文件。

  

  我们查看init.zygote64_32.rc:

service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
    class main
    socket zygote stream  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

service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary
    class main
    socket zygote_secondary stream  root system
    onrestart restart zygote
    writepid /dev/cpuset/foreground/tasks

  可以看到zygote的class是main, 它是在on nonencrypted时被启动的,如下:

on boot
    # basic network init
    ifup lo
    hostname localhost
    domainname localdomain
    ... ...
    class_start core

on nonencrypted
    # A/B update verifier that marks a successful boot.
    exec - root cache -- /system/bin/update_verifier nonencrypted
    class_start main
    class_start late_start

  至此,Init.cpp的main函数分析完毕!init进程已经启动完成,一些重要的服务如core服务和main服务也都启动起来,并启动了zygote(/system/bin/app_process64)进程,zygote初始化时会创建虚拟机,启动systemserver等。

Android 7.0 启动篇 — init原理(二)(转 Android 9.0 分析)的更多相关文章

  1. Android 7.0 启动篇 — init原理(一)(转 Android 9.0 分析)

    ========================================================          ================================== ...

  2. Android启动篇 — init原理(二)

    ========================================================          ================================== ...

  3. Android启动篇 — init原理(一)

    ========================================================          ================================== ...

  4. 【Android开发日记】之入门篇(十二)——Android组件间的数据传输

    组件我们有了,那么我们缺少一个组件之间传递信息的渠道.利用Intent做载体,这是一个王道的做法.还有呢,可以利用文件系统来做数据共享.也可以使用Application设置全局数据,利用组件来进行控制 ...

  5. 【Android Studio安装部署系列】十二、Android studio代码混淆

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 概述 为什么需要代码混淆呢?原因很简单,你的apk很容易被反编译出来,你写的代码都会被看到,因此我们需要在编译过程中对代码进行一定程度的混 ...

  6. Android开发环境的搭建之(二)Android Studio的安装

    (1)  下载AS(android studio)1.3.2并安装android-studio-bundle-141.2178183-windows.exe.下载官方链接http://www.andr ...

  7. Android启动脚本init.rc(2)

    在Android中使用启动脚本init.rc,可以在系统的初始化中进行简单的操作. init.rc启动脚本路径:system/core/rootdir/init.rc 内容: Commands:命令 ...

  8. init进程 && 解析Android启动脚本init.rc && 修改它使不启动android && init.rc中启动一个sh文件

    Android启动后,系统执行的第一个进程是一个名称为init 的可执行程序.提供了以下的功能:设备管理.解析启动脚本.执行基本的功能.启动各种服务.代码的路径:system/core/init,编译 ...

  9. [Android] Android 锁屏实现与总结 (二)

    上接: [Android] Android 锁屏实现与总结 (一) 系列文章链接如下: [Android] Android 锁屏实现与总结 (一) [Android] Android 锁屏实现与总结 ...

随机推荐

  1. Java 锁机制 synchronized

    转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/75126630 本文出自[赵彦军的博客] 1.前言 在多线程并发编程中Synchro ...

  2. DDGScreenShot — 复杂屏幕截屏(如view ScrollView webView wkwebView)

    写在前面 最近有这么一个需求,分享页面,分享的是web订单截图,既然是web 就会有超出屏幕的部分, 生成的图片还要加上我们的二维码,这就涉及到图片的合成了. 有了这样的需求,就是各种google.也 ...

  3. JS中$含义和用法

    原博客:https://www.cnblogs.com/jokerjason/p/7404649.html$在JS中本身只是一个符号而异,在JS里什么也不是.但在JS应用库JQUERY的作者将之做为一 ...

  4. ArcCore重构-Makefile模块化

    基于官方arc-stable-9c57d86f66be,AUTOSAR版本3.1.5   基本问题 2. 编译系统中代码文件是否编译及目标文件集中定义在boards/board_common.mk,而 ...

  5. 重温《STL源码剖析》笔记 第一章

    源码之前,了无秘密. --侯杰 经典的书,确实每看一遍都能重新收获一遍: 第一章:STL简介 STL的设计思维:对象的耦合性极低,复用性极高,符合开发封闭原则的程序库. STL的价值:1.带给我们一套 ...

  6. 深入理解SpringBoot之自动装配

    SpringBoot的自动装配是拆箱即用的基础,也是微服务化的前提.其实它并不那么神秘,我在这之前已经写过最基本的实现了,大家可以参考这篇文章.这次主要的议题是,来看看它是怎么样实现的,我们透过源代码 ...

  7. System Error. Code:1722. RPC服务器不可用解决办法

    原文链接(转载请注明出处):System Error. Code:1722. RPC服务器不可用解决办法 问题 最近在软件设计上机课的时候,使用 starUML 建模工具画UML图的时候总是弹出一条如 ...

  8. SpringMVC中Json数据格式转换

    1    @RequestBody 作用: @RequestBody注解用于读取http请求的内容(字符串),通过springmvc提供的HttpMessageConverter接口将读到的内容转换为 ...

  9. win7 中如何设置eclipse的背景色--Console

    http://blog.csdn.net/u013161399/article/details/47297781

  10. SSM-MyBatis-17:Mybatis中一级缓存(主要是一级缓存存在性的证明,增删改对一级缓存会造成什么影响)

    ------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 缓存------------------------------------------> 很熟悉的一个 ...