《深入理解Android 卷Ⅰ》深入理解init

深入理解init

1.1 概述

init 是一个进程,确切地说,它是Linux 系统中用户空间地第一个进程。由于Android 是基于Linux 内核的,所以init 也是Android 系统中用户空间的第一个进程,它的进程号是1。它的主要指责为:

  • init 进程负责创建系统中的几个关键进程,包括zygote。
  • Android 系统有很多属性,于是init 就提供了一个property service(属性服务)来管理它们。

1.2 init 分析

init 进程的入口函数是main,它的代码如下所示:

/*
 * 1.C++中主函数有两个参数,第一个参数argc表示参数个数,第二个参数是参数列表,也就是具体的参数
 * 2.init的main函数有两个其它入口,一是参数中有ueventd,进入ueventd_main,二是参数中有watchdogd,进入watchdogd_main
 */
int main(int argc, char** argv) {
    /*
     * 1.strcmp是String的一个函数,比较字符串,相等返回0
     * 2.C++中0也可以表示false
     * 3.basename是C库中的一个函数,得到特定的路径中的最后一个'/'后面的内容,
     * 比如/sdcard/miui_recovery/backup,得到的结果是backup
     */
    if (!strcmp(basename(argv[0]), "ueventd")) {
        //当argv[0]的内容为ueventd时,strcmp的值为0,!strcmp为1
        //1表示true,也就执行ueventd_main,ueventd主要是负责设备节点的创建、权限设定等一些列工作
        return ueventd_main(argc, argv);
    }

    if (!strcmp(basename(argv[0]), "watchdogd")) {
        //watchdogd俗称看门狗,用于系统出问题时重启系统
        return watchdogd_main(argc, argv);
    }

    if (argc > 1 && !strcmp(argv[1], "subcontext")) {
        InitKernelLogging(argv);
        const BuiltinFunctionMap function_map;
        return SubcontextMain(argc, argv, &function_map);
    }

    if (REBOOT_BOOTLOADER_ON_PANIC) {
        //初始化重启系统的处理信号,内部通过sigaction 注册信号,当监听到该信号时重启系统
        InstallRebootSignalHandlers();
    }

    bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr); //查看是否有环境变量INIT_SECOND_STAGE

    /*
     * init的main方法会执行两次,由is_first_stage控制,first_stage就是第一阶段要做的事
     */
    if (is_first_stage) { //只执行一次,因为在方法体中有设置INIT_SECOND_STAGE
        boot_clock::time_point start_time = boot_clock::now();

        // Clear the umask.
        umask(0); //清空文件权限

        clearenv();
        setenv("PATH", _PATH_DEFPATH, 1); //注册环境变量PATH
        // 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.
        // 创建一些文件,并挂载设备,这些是与Linux 相关的
        mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
        mkdir("/dev/pts", 0755);
        mkdir("/dev/socket", 0755);
        mount("devpts", "/dev/pts", "devpts", 0, NULL);
        #define MAKE_STR(x) __STRING(x)
        mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
        // Don't expose the raw commandline to unprivileged processes.
        chmod("/proc/cmdline", 0440);
        gid_t groups[] = { AID_READPROC };
        setgroups(arraysize(groups), groups);
        mount("sysfs", "/sys", "sysfs", 0, NULL);
        mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);

        mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));

        if constexpr (WORLD_WRITABLE_KMSG) {
            mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11));
        }

        mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8));
        mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9));
        // Mount staging areas for devices managed by vold
        // See storage config details at http://source.android.com/devices/storage/
        mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
              "mode=0755,uid=0,gid=1000");
        // /mnt/vendor is used to mount vendor-specific partitions that can not be
        // part of the vendor partition, e.g. because they are mounted read-write.
        mkdir("/mnt/vendor", 0755);

        // Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
        // talk to the outside world...
        // InitKernelLogging 首先是将标准输入输出重定向到"/sys/fs/selinux/null",然后调用InitLogging初始化log日志系统
        InitKernelLogging(argv);

        LOG(INFO) << "init first stage started!";

        if (!DoFirstStageMount()) { // 主要作用是初始化特定设备并挂载
            LOG(FATAL) << "Failed to mount required partitions early ...";
        }
        //Avb即Android Verfied boot,功能包括Secure Boot, verfying boot 和 dm-verity, 
        //原理都是对二进制文件进行签名,在系统启动时进行认证,确保系统运行的是合法的二进制镜像文件。
        //其中认证的范围涵盖:bootloader,boot.img,system.img
        SetInitAvbVersionInRecovery(); //在刷机模式下初始化avb的版本,不是刷机模式

        // Enable seccomp if global boot option was passed (otherwise it is enabled in zygote).
        global_seccomp();

        // Set up SELinux, loading the SELinux policy.
        SelinuxSetupKernelLogging();
        SelinuxInitialize(); //加载SELinux policy,也就是安全策略,

        // We're in the kernel domain, so re-exec init to transition to the init domain now
        // that the SELinux policy has been loaded.
        if (selinux_android_restorecon("/init", 0) == -1) { //restorecon命令用来恢复SELinux文件属性即恢复文件的安全上下文
            PLOG(FATAL) << "restorecon failed of /init failed";
        }

        setenv("INIT_SECOND_STAGE", "true", 1);

        static constexpr uint32_t kNanosecondsPerMillisecond = 1e6;
        uint64_t start_ms = start_time.time_since_epoch().count() / kNanosecondsPerMillisecond;
        setenv("INIT_STARTED_AT", std::to_string(start_ms).c_str(), 1); //记录第二阶段开始时间戳

        char* path = argv[0];
        char* args[] = { path, nullptr };
        execv(path, args); //重新执行main方法,进入第二阶段

        // execv() only returns if an error happened, in which case we
        // panic and never fall through this conditional.
        PLOG(FATAL) << "execv(\"" << path << "\") failed";
    }
    ...
}

在init初始化过程中,Android分别挂载了tmpfs,devpts,proc,sysfs,selinuxfs这5类文件系统。

  • tmpfs

tmpfs是一种虚拟内存文件系统,它会将所有的文件存储在虚拟内存中,如果你将tmpfs文件系统卸载后,那么其下的所有的内容将不复存在。
tmpfs既可以使用RAM,也可以使用交换分区,会根据你的实际需要而改变大小。
tmpfs的速度非常惊人,毕竟它是驻留在RAM中的,即使用了交换分区,性能仍然非常卓越。
由于tmpfs是驻留在RAM的,因此它的内容是不持久的。断电后,tmpfs的内容就消失了,这也是被称作tmpfs的根本原因。

  • devpts

devpts文件系统为伪终端提供了一个标准接口,它的标准挂接点是/dev/ pts。
只要pty的主复合设备/dev/ptmx被打开,就会在/dev/pts下动态的创建一个新的pty设备文件。

  • proc

proc文件系统是一个非常重要的虚拟文件系统,它可以看作是内核内部数据结构的接口,通过它我们可以获得系统的信息,同时也能够在运行时修改特定的内核参数。

  • sysfs

与proc文件系统类似,sysfs文件系统也是一个不占有任何磁盘空间的虚拟文件系统。
它通常被挂接在/sys目录下。sysfs文件系统是Linux2.6内核引入的,
它把连接在系统上的设备和总线组织成为一个分级的文件,使得它们可以在用户空间存取。

  • selinuxfs

selinuxfs也是虚拟文件系统,通常挂载在/sys/fs/selinux目录下,用来存放SELinux安全策略文件

init进程第一阶段做的主要工作是挂载分区,创建设备节点和一些关键目录,初始化日志输出系统,启用SELinux安全策略

第二阶段

int main(int argc, char** argv) {
    ...
    // At this point we're in the second stage of init.
    InitKernelLogging(argv);
    LOG(INFO) << "init second stage started!";

    // Set up a session keyring that all processes will have access to. It
    // will hold things like FBE encryption keys. No process should override
    // its session keyring.
    keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 1);//初始化进程会话密钥

    // Indicate that booting is in progress to background fw loaders, etc.
    close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));//创建 /dev/.booting 文件,就是个标记,表示booting进行中

    property_init();//初始化属性系统,并从指定文件读取属性

    //接下来的一系列操作都是从各个文件读取一些属性,然后通过property_set设置系统属性
    // If arguments are passed both on the command line and in DT,
    // properties set in DT always have priority over the command-line ones.
    /*
     * 1.这句英文的大概意思是,如果参数同时从命令行和DT传过来,DT的优先级总是大于命令行的
     * 2.DT即device-tree,中文意思是设备树,这里面记录自己的硬件配置和系统运行参数
     */
    process_kernel_dt();//处理DT属性
    process_kernel_cmdline();//处理命令行属性

    // Propagate the kernel variables to internal variables
    // used by init as well as the current required properties.
    export_kernel_boot_props();//处理其他的一些属性

    // Make the time that init started available for bootstat to log.
    property_set("ro.boottime.init", getenv("INIT_STARTED_AT"));
    property_set("ro.boottime.init.selinux", getenv("INIT_SELINUX_TOOK"));

    // Set libavb version for Framework-only OTA match in Treble build.
    const char* avb_version = getenv("INIT_AVB_VERSION");
    if (avb_version) property_set("ro.boot.avb_version", avb_version);

    // Set memcg property based on kernel cmdline argument
    bool memcg_enabled = android::base::GetBoolProperty("ro.boot.memcg",false);
    if (memcg_enabled) {
       // root memory control cgroup
       mkdir("/dev/memcg", 0700);
       chown("/dev/memcg",AID_ROOT,AID_SYSTEM);
       mount("none", "/dev/memcg", "cgroup", 0, "memory");
       // app mem cgroups, used by activity manager, lmkd and zygote
       mkdir("/dev/memcg/apps/",0755);
       chown("/dev/memcg/apps/",AID_SYSTEM,AID_SYSTEM);
       mkdir("/dev/memcg/system",0550);
       chown("/dev/memcg/system",AID_SYSTEM,AID_SYSTEM);
    }

    // Clean up our environment.
    unsetenv("INIT_SECOND_STAGE");
    unsetenv("INIT_STARTED_AT");
    unsetenv("INIT_SELINUX_TOOK");
    unsetenv("INIT_AVB_VERSION");

    // Now set up SELinux for second stage.
    SelinuxSetupKernelLogging(); //第二阶段初始化SELinux policy
    SelabelInitialize();
    SelinuxRestoreContext(); //恢复安全上下文

    epoll_fd = epoll_create1(EPOLL_CLOEXEC); //创建epoll实例,并返回epoll的文件描述符
    if (epoll_fd == -1) {
        PLOG(FATAL) << "epoll_create1 failed";
    }

    sigchld_handler_init(); //主要是创建handler处理子进程终止信号,创建一个匿名socket并注册到epoll进行监听

    if (!IsRebootCapable()) {
        // If init does not have the CAP_SYS_BOOT capability, it is running in a container.
        // In that case, receiving SIGTERM will cause the system to shut down.
        InstallSigtermHandler();
    }

    property_load_boot_defaults(); //从文件中加载一些属性,读取usb配置
    export_oem_lock_status(); //设置ro.boot.flash.locked 属性
    start_property_service(); //开启一个socket监听系统属性的设置
    set_usb_controller(); //设置sys.usb.controller 属性
    
    ...
}

init进程第二阶段主要工作是初始化属性系统,解析SELinux的匹配规则,处理子进程终止信号,启动系统属性服务,可以说每一项都很关键,如果说第一阶段是为属性系统,SELinux做准备,那么第二阶段就是真正去把这些落实的。

init.rc

在该函数里面,还会解析一个叫init.rc 的配置文件。

init.rc是init进程启动的配置脚本,这个脚本是用一种叫Android Init Language(Android初始化语言)的语言写的,在7.0以前,init进程只解析根目录下的init.rc文件,但是随着版本的迭代,init.rc越来越臃肿,所以在7.0以后,init.rc一些业务被分拆到/system/etc/init,/vendor/etc/init,/odm/etc/init三个目录下,

Android Init Language语法定义在platform/system/core/init/README.md

.rc 文件主要配置了两个东西,一个是action,一个是service,trigger 和command 是对action 的补充,options 是对service 的补充。action 加上trigger 以及一些command,组成一个Section,service加上一些option,也组成一个Section,.rc文件就是由一个个Section组成。.rc文件头部有一个import的语法,表示这些.rc也一并包含并解析。

Init .rc Files

/init.rc 是最主要的一个.rc文件,它由init进程在初始化时加载,主要负责系统初始化,它会导入 /init.${ro.hardware}.rc ,这个是系统级核心厂商提供的主要.rc文件。当执行 mount_all 语句时,init进程将加载所有在 /{system,vendor,odm}/etc/init/ 目录下的文件,挂载好文件系统后,这些目录将会为Actions和Services服务。
有一个特殊的目录可能被用来替换上面的三个默认目录,这主要是为了支持工厂模式和其他非标准的启动模式,上面三个目录用于正常的启动过程,这三个用于扩展的目录是:

  • /system/etc/init/ 用于系统本身,比如SurfaceFlinger, MediaService, and logcatd.
  • /vendor/etc/init/ 用于SoC(系统级核心厂商,如高通),为他们提供一些核心功能和服务
  • /odm/etc/init/ 用于设备制造商(odm定制厂商,如华为、小米),为他们的传感器或外围设备提供一些核心功能和服务

1.3 解析.rc 文件

int main(int argc, char** argv) {
    ...
    LoadBootScripts(am, sm);
    ...
}
// 加载和解析init.rc 文件
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()) {
        // 解析init.rc 文件
        parser.ParseConfig("/init.rc");
        if (!parser.ParseConfig("/system/etc/init")) {
            late_import_paths.emplace_back("/system/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);
    }
}

主要解析工作是在/system/core/init/parser.cpp

void Parser::ParseData(const std::string& filename, const std::string& data, size_t* parse_errors) {
    ...
    //lambda表达式,函数的引用,每次解析新的一行之前
    //都会调用这个将上一行解析的结果放到对应的数组中
    auto end_section = [&] {
        if (section_parser == nullptr) return;

        if (auto result = section_parser->EndSection(); !result) {
            (*parse_errors)++;
            LOG(ERROR) << filename << ": " << section_start_line << ": " << result.error();
        }

        //重置
        section_parser = nullptr;
        section_start_line = -1;
    };
    ...
}

具体解析过程在后面的一个无限for 循环里面:

void Parser::ParseData(const std::string& filename, const std::string& data, size_t* parse_errors) {
    ...
    for (;;) {
        switch (next_token(&state)) {
            case T_EOF:
                end_section(); // 这里就是调用上面的end_section
                return;
            case T_NEWLINE:
                state.line++;
                if (args.empty()) break;
                // If we have a line matching a prefix we recognize, call its callback and unset any
                // current section parsers.  This is meant for /sys/ and /dev/ line entries for
                // uevent.
                for (const auto& [prefix, callback] : line_callbacks_) {
                    if (android::base::StartsWith(args[0], prefix)) {
                        end_section();

                        if (auto result = callback(std::move(args)); !result) {
                            (*parse_errors)++;
                            LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                        }
                        break;
                    }
                }
                if (section_parsers_.count(args[0])) { //判断是否包含 on service import
                    end_section();
                    section_parser = section_parsers_[args[0]].get(); //取出对应的parser
                    section_start_line = state.line;
                    if (auto result =
                            section_parser->ParseSection(std::move(args), filename, state.line);
                        !result) {
                        (*parse_errors)++;
                        LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                        section_parser = nullptr;
                    }
                } else if (section_parser) { //不包含 on service import则是command或option
                    if (auto result = section_parser->ParseLineSection(std::move(args), state.line);
                        !result) {
                        (*parse_errors)++;
                        LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                    }
                }
                args.clear();
                break;
            case T_TEXT:
                args.emplace_back(state.text);
                break;
        }
    }
}

next_token扫描init.rc中的token

找到其中的文件结束(EOF)、文本(TEXT)、新行(NEWLINE),其中的空格\t\r会被忽略掉;对于TEXT,空格\t\r\n都是TEXT的结束标志。

section_parsers_.count(args[0])

会判断args[]第一个字符是不是on、service或import
如果是就拿到对应的解析器,例如是service开头的新行,section_parser就是对应之前添加的ServcieParser解析器,并且执行auto result = section_parser-> ParseSection(std::move(args), filename, state.line);,否则就执行auto result = section_parser-> ParseLineSection(std::move(args), state.line);

ParseLineSection是解析service下面附带的option(例如oneshot,class等等),并将这些属性添加到Service对象中)。

解析完一个Service对象后
end_section()中的section_parser->EndSection()将Service对象添加到Service List(services_)中,并且重置section_parser = null,为解析下一个Service做准备。

on开头的使用ActionParser解析器解析,该解析器的ParseSection方法主要是生成action对象,并将action对象添加到actions_数组中,ParseLineSection方法主要是解析command对应的函数,然后添加到action对象的commands_数组中。ServiceParser的ParseLineSection是直接执行option对应函数,而ActionParser是将对应函数保存到commands_数组中,当Action触发时,才会依次执行command函数。

总共有三个解析器,分别是:

  • ActionParser

定义在platform/system/core/init/action.cpp

  • ServiceParser

定义在platform/system/core/init/service.cpp

  • ImportParser

定义在platform/system/core/init/import_parser.cpp

init.rc解析大致如此。

1.4 触发条件

经过上一步的解析,系统从各种.rc文件中读取了需要执行的Action和Service,但是还是需要一些额外的配置,也需要加入触发条件准备去触发。

int main(int argc, char** argv) {
    ...
    // Turning this on and letting the INFO logging be discarded adds 0.2s to
    // Nexus 9 boot time, so it's disabled by default.
    if (false) parser.DumpState(); //打印一些当前Parser的信息,默认是不执行的

    ActionManager& am = ActionManager::GetInstance();

    am.QueueEventTrigger("early-init");//QueueEventTrigger用于触发Action,这里触发 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");
    //QueueBuiltinAction用于添加Action,第一个参数是Action要执行的Command,第二个是Trigger

    // ... 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(set_mmap_rnd_bits_action, "set_mmap_rnd_bits");
    am.QueueBuiltinAction(set_kptr_restrict_action, "set_kptr_restrict");
    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");

    // 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.
    std::string bootmode = GetProperty("ro.bootmode", "");
    if (bootmode == "charger") {
        am.QueueEventTrigger("charger");
    } else {
        am.QueueEventTrigger("late-init");
    }

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

定义在platform/system/core/init/action.cpp
它并没有去触发trigger,而是构造了一个EventTrigger对象,放到队列中存起来

QueueBuiltinAction

定义在platform/system/core/init/action.cpp
这个函数有两个参数,第一个参数是一个函数指针,第二参数是字符串。首先是创建一个Action对象,将第二参数作为Action触发条件,将第一个参数作为Action触发后的执行命令,并且又把第二个参数作为命令的参数,最后是将Action加入触发队列并加入Action列表。

1.5 触发及监听

之前的所有工作都是往各种数组、队列里面存入信息,并没有真正去触发,而接下来的工作就是真正去触发这些事件,以及用epoll不断监听新的事件。

int main(int argc, char** argv) {
   ...
   while (true) {
       // By default, sleep until something happens.
       int epoll_timeout_ms = -1; //epoll超时时间,相当于阻塞时间

       if (do_shutdown && !shutting_down) {
           do_shutdown = false;
           if (HandlePowerctlMessage(shutdown_command)) {
               shutting_down = true;
           }
       }
        /*
         * 1.waiting_for_prop和IsWaitingForExec都是判断一个Timer为不为空,相当于一个标志位
         * 2.waiting_for_prop负责属性设置,IsWaitingForExe负责service运行
         * 3.当有属性设置或Service开始运行时,这两个值就不为空,直到执行完毕才置为空
         * 4.其实这两个判断条件主要作用就是保证属性设置和service启动的完整性,也可以说是为了同步
         */
       if (!(waiting_for_prop || Service::is_exec_service_running())) {
           am.ExecuteOneCommand(); //执行一个command
       }
       if (!(waiting_for_prop || Service::is_exec_service_running())) {
           if (!shutting_down) {
               auto next_process_restart_time = RestartProcesses(); //重启服务

               // If there's a process that needs restarting, wake up in time for that.
               if (next_process_restart_time) {
                   epoll_timeout_ms = std::chrono::ceil<std::chrono::milliseconds>(
                                          *next_process_restart_time - boot_clock::now())
                                          .count();
                   if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
               }
           }

           // If there's more work to do, wake up again immediately.
           if (am.HasMoreCommands()) epoll_timeout_ms = 0;  //当还有命令要执行时,将epoll_timeout_ms设置为0
       }

       epoll_event ev;
       /*
        * 1.epoll_wait与上一篇中讲的epoll_create1、epoll_ctl是一起使用的
        * 2.epoll_create1用于创建epoll的文件描述符,epoll_ctl、epoll_wait都把它创建的fd作为第一个参数传入
        * 3.epoll_ctl用于操作epoll,EPOLL_CTL_ADD:注册新的fd到epfd中,EPOLL_CTL_MOD:修改已经注册的fd的监听事件,EPOLL_CTL_DEL:从epfd中删除一个fd;
        * 4.epoll_wait用于等待事件的产生,epoll_ctl调用EPOLL_CTL_ADD时会传入需要监听什么类型的事件,
        * 比如EPOLLIN表示监听fd可读,当该fd有可读的数据时,调用epoll_wait经过epoll_timeout_ms时间就会把该事件的信息返回给&ev
        */
       int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, epoll_timeout_ms));
       if (nr == -1) {
           PLOG(ERROR) << "epoll_wait failed";
       } else if (nr == 1) {
           ((void (*)()) ev.data.ptr)();
       }
   }     
    ...
}
ExecuteOneCommand

定义在platform/system/core/init/action.cpp
从名字可以看出,它只执行一个command,只执行一个。在函数一开始就从trigger_queue_队列中取出一个trigger,然后遍历所有action,找出满足trigger条件的action加入待执行列表current_executing_actions_中,接着从这个列表中取出一个action,执行它的第一个命令,并将命令所在下标自加1。由于ExecuteOneCommand外部是一个无限循环,因此按照上面的逻辑一遍遍执行,将按照trigger表的顺序,依次执行满足trigger条件的action,然后依次执行action中的命令.

RestartProcesses

定义在platform/system/core/init/init.cpp
restart_processes调用的其实是ForEachServiceWithFlags函数,这个函数主要是遍历services_数组,比较它们的flags是否是SVC_RESTARTING,也就是当前service是否是等待重启的,如果是就执行它的RestartIfNeeded函数。

RestartIfNeeded

定义在platform/system/core/init/service.cpp
这个函数将主要工作交给了Start,也就是具体的启动service,但是交给它之前做了一些判断,也就是5秒内只能启动一个服务,如果有多个服务,那么后续的服务将进入等待。

总结

Init进程根据语法一步步去解析.rc,将这些配置转换成一个个数组、队列,然后开启无限循环去处理这些数组、队列中的command和service,并且通过epoll监听子进程结束和属性设置。


参考文献
[1] 邓平凡.深入理解Android-卷Ⅰ.北京:机械工业出版社.2011-9
[2] Android 8.0 : 系统启动流程之init进程
[3] Android P (9.0) 之Init进程源码分析

你可能感兴趣的:(深入理解Android,Android,init,AndroiP)