Init .rc 的初始化顺序

孙和
• 阅读 7516

之前整理过Android 7的初始化顺序,已经过时了,基于Android 11重新整理下并发布,方便查看与rc里初始化顺序有关的问题和bug.

<!-- more -->

[Android 11]
代码可看
http://aosp.opersys.com/xref/...

初始化语言可看

/system/core/init/README.md

或者

https://blog.csdn.net/u010753159/article/details/51981121  

这里不讲这些, 也不分析init代码.

init rc 总体初始化顺序

    FirstStageMain()
         |
         v
    SetupSelinux()
         |
         v
    SecondStageMain()
         |
         v
+----------------------+
|    SetupCgroups      |
|   SetKptrRestrict    |
| TestPerfEventSelinux |
|      early-init      +--> mount_all fstab.persist --early // hardware/google/pixel/common/init.pixel.rc
|wait_for_coldboot_done|
| MixHwrngIntoLinuxRng |
|    SetMmapRndBits    |
|     KeychordInit     |
|         init         |
| MixHwrngIntoLinuxRng |
|          |           |
| bootmode == "charger"|
|       /    \         |
|      +      +        |
|   charger late-init  +--> +--------trigger---------+
|       \   /          |    |       early-fs         |
|         +            |    |         fs             +--> mount_all fstab.firmware --early
|         |            |    |       late-fs          +--> +--------------------------------+
|queue_property_triggers|   |       post-fs          |    |mount_all fstab.firmware --late +--> queue_fs_event --> trigger nonencrypted
+-----------------------+   |     post-fs-data       |    |   class_start early_hal        |                               |
                            |load_persist_props_action|   +--------------------------------+                       +-class_start-+
                            |    load_bpf_programs   |                                                             |     main    |
                            |      zygote-start      |                                                             |  late_start |
                            |firmware_mounts_complete|                                                             +-------------+
                            |       early-boot       |                                                                     ^
                            |         boot           +--> +-class_start-+                                                  |
                            +------------------------+    |     hal     |                                                  |
                                       |                  |     core    |                                                  |
                                       |                  + ------------+                                                  |
                                       +------------------------------------------执行完 on boot后才执行-------------------- -+

on late-init 和 late-fs 启动具体代码

/system/core/rootdir/init.rc
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

    # 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

    # Should be before netd, but after apex, properties and logging is available.
    trigger load_bpf_programs

    # Now we can start zygote for devices with file based encryption
    trigger zygote-start

    # Remove a file to wake up anything waiting for firmware.
    trigger firmware_mounts_complete

    trigger early-boot
    trigger boot

// late-fs
on late-fs
    # Ensure that tracefs has the correct permissions.
    # This does not work correctly if it is called in post-fs.
    chmod 0755 /sys/kernel/tracing
    chmod 0755 /sys/kernel/debug/tracing

    # HALs required before storage encryption can get unlocked (FBE/FDE)
    class_start early_hal

early_hal类服务有:

early_hal
/system/hardware/interfaces/suspend/1.0/default/android.system.suspend@1.0-service.rc
/system/core/trusty/keymaster/3.0/android.hardware.keymaster@3.0-service.trusty.rc
/system/core/trusty/keymaster/4.0/android.hardware.keymaster@4.0-service.trusty.rc

注意:

QueueEventTrigger和QueueBuiltinAction都是将其放放到event_queue_里,等轮到他时再执行

/system/core/init/action_manager.cpp
void ActionManager::QueueEventTrigger(const std::string& trigger) {
    auto lock = std::lock_guard{event_queue_lock_};
    // 加入到 event_queue_队列里
    event_queue_.emplace(trigger);
}
......
void ActionManager::QueueBuiltinAction(BuiltinFunction func, const std::string& name) {
    auto lock = std::lock_guard{event_queue_lock_};
    auto action = std::make_unique<Action>(true, nullptr, "<Builtin Action>", 0, name,
                                           std::map<std::string, std::string>{});
    action->AddCommand(std::move(func), {name}, 0);
    // 加入到 event_queue_队列里
    event_queue_.emplace(action.get());
    actions_.emplace_back(std::move(action));
}

也就是说在
on late-fs --> mount_all --> trigger nonencrypted
的时候,并没有执行 on nonencrypted
而是等到之前队列里的trigger执行完后才执行它, 我们看到 on late-init 时加了一堆的trigger,
最后个是boot, 所以(如果中间没新加入的) on nonencrypted 是在 on boot 之后才执行的, 并不是在 on late-fs之后执行的.

也即 class mainclass late_start在 on boot后执行

on late-init
    trigger early-fs
    trigger fs
    trigger post-fs
    trigger late-fs
    ....
    trigger boot

// on boot后执行
on nonencrypted
    class_start main
    class_start late_start

imports 与 目录里的相同Action执行顺序

我们知道,不同rc文件文件里是可以有相同的action的,init程序在解析时会将这些不同文件的action合并,
那这些合并后的action执行顺序是咋样的?

 BTW, 解析rc文件有兴趣的可以看看, 相当于是个小型的语法解析.
 代码在 /system/core/init/parser.cpp ParseData()

具体的说,就是

  • init.rc 与 其 import导入的文件里相同action执行顺序
  • /{system,vendor,odm}/etc/init/ 里相同action执行顺序

这个其实在/system/core/init/README.md Imports 章节都有描述的, 建议仔细的看看.

简单来说,其先后顺序为

init.rc -> init.rc里的import -> import 本身 -> import的import -> /system/etc/init/ -> /vendor/etc/init/ -> /odm/etc/init/
                                 ^                ^
                                 +--------递归-----+

README.md用伪代码里描述如下:

fn Import(file)
  Parse(file)
  for (import : file.imports)
    Import(import)

Import(/init.rc)
Directories = [/system/etc/init, /vendor/etc/init, /odm/etc/init]
for (directory : Directories)
  files = <Alphabetical order of directory's contents>
  for (file : files)
    Import(file)

具体的代码可看下

/system/core/init/init.cpp
SecondStageMain() --> LoadBootScripts()
static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
    Parser parser = CreateParser(action_manager, service_list);

    std::string bootscript = GetProperty("ro.boot.init_rc", "");
    if (bootscript.empty()) {
        parser.ParseConfig("/system/etc/init/hw/init.rc");
        if (!parser.ParseConfig("/system/etc/init")) {
            late_import_paths.emplace_back("/system/etc/init");
        }
        // late_import is available only in Q and earlier release. As we don't
        // have system_ext in those versions, skip late_import for system_ext.
        parser.ParseConfig("/system_ext/etc/init");
        if (!parser.ParseConfig("/product/etc/init")) {
            late_import_paths.emplace_back("/product/etc/init");
        }
        if (!parser.ParseConfig("/odm/etc/init")) {
            late_import_paths.emplace_back("/odm/etc/init");
        }
        if (!parser.ParseConfig("/vendor/etc/init")) {
            late_import_paths.emplace_back("/vendor/etc/init");
        }
    } else {
        parser.ParseConfig(bootscript);
    }
}

从代码里我们可看到,如果指定了ro.boot.init_rc 就用指定的,
否则就用默认那几个目录的, 其中还有对 /system/etc/init/hw/init.rcsystem_ext 目录支持.
图就不画了,看看代码就好.

  • 对同一个目录下文件解析, 其实是按升序解析的

所以如果同一目录下有相同的action, 要按照文件名升序先后执行的.

代码如下

/system/core/init/parser.cpp
bool Parser::ParseConfigDir(const std::string& path) {
    LOG(INFO) << "Parsing directory " << path << "...";
......
    std::vector<std::string> files;
    // 获取目录下文件
    while ((current_file = readdir(config_dir.get()))) {
        // Ignore directories and only process regular files.
        if (current_file->d_type == DT_REG) {
            std::string current_path =
                android::base::StringPrintf("%s/%s", path.c_str(), current_file->d_name);
            files.emplace_back(current_path);
        }
    }
    // 按升序排序
    // Sort first so we load files in a consistent order (bug 31996208)
    std::sort(files.begin(), files.end());
    // 挨个解析排序后的文件
    for (const auto& file : files) {
        if (!ParseConfigFile(file)) {
            LOG(ERROR) << "could not import file '" << file << "'";
        }
    }
    return true;
}

执行顺序调节

通过以上分析,我们大致的知道了这些rc文件和action执行先后顺序,
如果有需要调节顺序的,可以从以下几个方面考虑

  • (自定义) 调节trigger顺序
  • 调节服务的 class 级别 (class main, class core ....)
  • 调节rc文件位置
  • 调节文件名按升序方式排在前面
点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
美凌格栋栋酱 美凌格栋栋酱
6个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
似梦清欢 似梦清欢
2年前
查找算法
顺序查找顺序查找又称为线性查找,对线性表和链表都适用。线性表可以通过数组下标递增来顺序扫描每个元素,链表可以通过next指针依次扫描每一个元素。:::tip指针实现顺序表时,顺序表中是指针时,在定义顺序表的结构体后,需要对顺序表初始化,初始化时为指针申请堆
Wesley13 Wesley13
3年前
java对象初始化的顺序
Java代码1.publicclassSonextendsFather {3.String value  null;//25.publicSon() {6.super();//17.System.out.println("Son:  " va
Wesley13 Wesley13
3年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Wesley13 Wesley13
3年前
4cast
4castpackageloadcsv.KumarAwanish发布:2020122117:43:04.501348作者:KumarAwanish作者邮箱:awanish00@gmail.com首页:
Stella981 Stella981
3年前
Linux安装mysql5.7版本
1.linux安装mysql5.7顺序①mysqladmin –version查看版本号②mysql5.7安装在linux中需要先初始化Mysqld –initialize –usermysql③查看初始化密码Cat /var/log/mysqld.log在@localhost后面的为初始化密码④启动mysqlSyst
Wesley13 Wesley13
3年前
KDE发布首个4.7 RC发行
KDEShipsFirst4.7ReleaseCandidateKDE发布首个4.7RC发行By:SebastianKügler,in:20110625,about;KDEOfficialNews发表于:2011年6月25日北京时间08:00KDEhasrelease
Wesley13 Wesley13
3年前
Java 初始化执行顺序以及成员变量初始化顺序
一、静态变量初始化顺序大家先看两个例子:(1)!(https://oscimg.oschina.net/oscnet/66be9168f7cdf36484b71f1d67069f12492.jpg)!(https://oscimg.oschina.net/oscnet/bf5d0b172f00f9aa237b3aee5b58cad5d0
Wesley13 Wesley13
3年前
C++中初始化的顺序问题
C的初始化顺序非常重要,牢记才能不出常识性的错误。其初始化顺序为:1类中的static成员是最先初始化的,这个是先于main函数的执行的,但是必须注意,如果这个成员只是在类中声明,而没有在类外边进行定义的话,那么这个是不会开辟内存的,是不会初始化的。2调用基类的构造函数。但是基类分为两种顺序,特别注意。一种是虚继承的基类;另一种是普通继承
孙和
孙和
Lv1
故乡今夜思千里,霜鬓明朝又一年。
文章
3
粉丝
0
获赞
0