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

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

【前言】

  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. 利用truffle与智能合约进行交互

    先了解相关指令,再观看比较合适:http://truffle.tryblockchain.org/ 安装: 先完成上一条博客的安装,再来进行下面的操作:http://www.cnblogs.com/t ...

  2. 启动eclipse时候提示错误Error:Could not create the Java Virtual Machine. Error:A Fatal exception has occurred,Program will exit.

    我的是neon3版本 解决办法是: 首先把这两个选项勾选,才能看到eclipse.ini完整的文件名.然后用记事本等工具打开编辑. 新版的里面原本是这样: -startup plugins/org.e ...

  3. 二叉树,AVL树和红黑树

    为了接下来能更好的学习TreeMap和TreeSet,讲解一下二叉树,AVL树和红黑树. 1. 二叉查找树 2. AVL树 2.1. 树旋转 2.1.1. 左旋和右旋 2.1.2. 左左,右右,左右, ...

  4. hdu-2683 TCE-frep number system---完全数+二项展开式

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=2683 题目大意: g(n)是n的因子和 两种操作: A a b 查询a b区间有多少个n满足上式. ...

  5. 最好的营销是“调情”

    每一个精彩的营销案例都应该像一个精彩的故事,而故事最精彩的讲述方式就是设置一个开放的结局,把解读和诠释的权利留给读者和观众.宣讲.洗脑式的营销时代已经终结,就像单相思的深情表白永远不如两情相悦的彼此挑 ...

  6. mysql索引sql优化方法、步骤和经验

    MySQL索引原理及慢查询优化 http://blog.jobbole.com/86594/ 细说mysql索引 https://www.cnblogs.com/chenshishuo/p/50300 ...

  7. D. Kuro and GCD and XOR and SUM

    Kuro is currently playing an educational game about numbers. The game focuses on the greatest common ...

  8. 在不重装系统的情况下创建Linux的Swap分区

    本文由荒原之梦原创,原文链接:http://zhaokaifeng.com/?p=649 操作环境: CentOS 7 操作背景: 本文中使用的CentOS Linux系统在安装的时候没有创建Swap ...

  9. Re:从零开始的领域驱动设计

    领域驱动的火爆程度不用我赘述,但是即便其如此得耳熟能详,但大多数人对其的认识,还只是停留在知道它的缩写是DDD,知道它是一种软件思想,或者知道它和微服务有千丝万缕的关系.Eric Evans对DDD的 ...

  10. Http Hijacker