起動概要#
Android の最初の起動プロセスで、init の PID は 1 です。このプロセスは init.rc を解析してシステムの初期動作形態を構築し、他のシステムが順次起動します。
電源起動&システム起動
電源が入ると、所定の場所(PC が ROM を読み取る)からプログラム(BootLoader)を RAM にロードし、実行を開始します。
ブートローダー BootLoader
ブートローダー BootLoader は Android システムが動作を開始する前の小さなプログラムで、主な機能はシステム OS を起動して実行することです。
Linux カーネルの起動
カーネルが起動すると、キャッシュを設定し、ストレージを保護し、スケジュールリストを計画し、ドライバをロードします… カーネルがシステム設定を完了すると、システム内で init.rc ファイルを探し、init プロセスを起動します。
init プロセスの起動
このプロセスは主に初期化とシステムプロパティサービスの起動を行い、Zygote プロセスを起動するためにも使用されます。
Launcher App アプリケーション(デスクトップアプリケーション)
init プロセスの概要#
init は Android システム起動の最初のプロセスとして、init.rc を解析して重要なシステムサービスプロセスを順次起動します。これには 3 つの重要なサービスプロセスがあります。
ServiceManager(DNS 機能に似ており、SystemServer 内のサービスハンドルを検索するために使用されます)
Zygote(初期プロセスで、必要な Android リソースをロードし、仮想マシンを起動します… など)
SystemServer(システムレベルのサービスを起動します。例えば AMS、WMS… サービス)
init プロセスは Zygote と SystemServer の 2 つのプロセスを起動し、Zygote は Socket を使用して AMS をリッスンし、ServiceManager は Binder 通信を使用します。
起動に必要なファイルディレクトリを作成し、マウントします。
サービスを初期化し、起動します。
init.rc 構成ファイルを解析し、Zygote プロセスを起動します。
init プロセス - main.cpp 分析#
// /init/main.cpp
#include "builtins.h"
#include "first_stage_init.h"
#include "init.h"
#include "selinux.h"
#include "subcontext.h"
#include "ueventd.h"
using namespace android::init;
... 省略部分
int main(int argc, char** argv) {
... 省略部分
// 後で復元される優先度をブーストします
setpriority(PRIO_PROCESS, 0, -20);
if (!strcmp(basename(argv[0]), "ueventd")) { // ueventdはデバイスノード、権限、匿名共有メモリなどを作成します
return ueventd_main(argc, argv);
}
if (argc > 1) {
if (!strcmp(argv[1], "subcontext")) {
// Loggerを初期化します
android::base::InitLogging(argv, &android::base::KernelLogger);
const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
return SubcontextMain(argc, argv, &function_map);
}
// Selinuxに関連するセキュリティ
if (!strcmp(argv[1], "selinux_setup")) {
return SetupSelinux(argv);
}
// initプロセス(第2段階)
if (!strcmp(argv[1], "second_stage")) {
return SecondStageMain(argc, argv);
}
}
// initプロセスを起動します(第1段階)
return FirstStageMain(argc, argv);
}
init プロセス第 1 段階 - first_stage_init#FirstStageMain
ファイルのマウント#
第 1 段階では主に 1. 関連するファイルを作成しマウントします。ここではいくつかの重要なファイルシステムを示します。詳細な機能はコメントを参照してください。2. 引数を設定します。3.execv 関数を実行します(再度 main.cpp をロードしますが、異なる引数を渡します)。
マウントされたファイルはすべてメモリ内(RAM)にあり、実体ファイルです。
使用コマンド | マウントディレクトリ / ファイル名、ファイル管理 | 特性 | 説明 |
---|---|---|---|
mount | tmpfs | RAM 内に存在し、永続的ではない(tmp の命名) | 仮想メモリファイルシステムで、すべてのファイルが仮想メモリ内に存在します。tmpfs をアンマウントすると、内部のデータはすべて消えます。 |
mount | /dev/devpts | 動的に devpts をマウント | 擬似端末に標準を提供し、pty の主複合デバイス /dev/ptmx が開かれると、/dev/pts に新しい pty デバイスファイルが動的に作成されます。 |
mount | proc | 実行時にカーネルパラメータを変更可能 | 仮想ファイルシステムで、カーネル内部データ構造のインターフェースと見なすことができます。 |
mount | sysfs | Linux 2.6 カーネルで導入(主にハードウェア関連のパラメータに関するもので、devpts デバイスに関連します) | 仮想ファイルシステムで、通常は /sys ディレクトリにマウントされます。 |
// first_stage_init.cpp
int FirstStageMain(int argc, char** argv) {
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers(); // initエラー時にBoot Loaderを再起動します
}
// 起動時間を記録します
boot_clock::time_point start_time = boot_clock::now();
// エラーリストを記録します
std::vector<std::pair<std::string, int>> errors;
#define CHECKCALL(x) \
if ((x) != 0) errors.emplace_back(#x " failed", errno);
// umaskをクリアし、以降に作成されるファイル、フォルダがすべての権限を持つようにします
umask(0);
CHECKCALL(clearenv());
CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));
// initramdisk内で必要な基本ファイルシステムのセットアップを行います
// /に配置し、その後rcファイルが残りを処理します。
// mountはtmpfsファイルシステムをマウントするために使用されます
CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));
// フォルダを作成し、権限を付与します
CHECKCALL(mkdir("/dev/pts", 0755));
CHECKCALL(mkdir("/dev/socket", 0755));
CHECKCALL(mkdir("/dev/dm-user", 0755));
CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL));
#define MAKE_STR(x) __STRING(x)
// procをマウントします
CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)));
#undef MAKE_STR
// 権限のないプロセスに生のコマンドラインを公開しないでください。
CHECKCALL(chmod("/proc/cmdline", 0440));
std::string cmdline;
android::base::ReadFileToString("/proc/cmdline", &cmdline);
// 権限のないプロセスに生のbootconfigを公開しないでください。
chmod("/proc/bootconfig", 0440);
std::string bootconfig;
android::base::ReadFileToString("/proc/bootconfig", &bootconfig);
// 8.0ではユーザーグループが追加されました
gid_t groups[] = {AID_READPROC};
// グループを現在のプロセスのデバイスに追加します
CHECKCALL(setgroups(arraysize(groups), groups));
// sysをマウントし、sysfsでドキュメントを管理します
CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));
CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL));
// ログ出力用のノードを作成します(mknod)
CHECKCALL(mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11)));
if constexpr (WORLD_WRITABLE_KMSG) {
CHECKCALL(mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11)));
}
CHECKCALL(mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8)));
CHECKCALL(mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9)));
// これはログラッパーに必要で、ueventdが実行される前に呼び出されます。
CHECKCALL(mknod("/dev/ptmx", S_IFCHR | 0666, makedev(5, 2)));
CHECKCALL(mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3)));
// ストレージ構成の詳細はhttp://source.android.com/devices/storage/を参照してください
// /mnt/{vendor,product}をマウントし、tmpfsで管理します
CHECKCALL(mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
"mode=0755,uid=0,gid=1000"));
// /mnt/vendorはvendorをマウントするために使用されます
CHECKCALL(mkdir("/mnt/vendor", 0755));
// /mnt/productは、製品パーティションの一部ではない製品固有のパーティションをマウントするために使用されます。例えば、読み書きでマウントされるためです。
CHECKCALL(mkdir("/mnt/product", 0755));
// /debug_ramdiskは、デバッグramdiskからの追加ファイルを保持するために使用されます
CHECKCALL(mount("tmpfs", "/debug_ramdisk", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
"mode=0755,uid=0,gid=0"));
// /second_stage_resourcesは、最初から第二段階initへのファイルを保持するために使用されます
CHECKCALL(mount("tmpfs", kSecondStageRes, "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
"mode=0755,uid=0,gid=0"))
#undef CHECKCALL
SetStdioToDevNull(argv);
InitKernelLogging(argv);
// エラーメッセージをリストします
if (!errors.empty()) {
for (const auto& [error_string, error_errno] : errors) {
LOG(ERROR) << error_string << " " << strerror(error_errno);
}
LOG(FATAL) << "Initは最初の段階の起動中にエラーに遭遇し、中止します";
}
... 省略部分
return 1;
}
特定のパーティションデバイスをマウント
// first_stage_init.cpp
int FirstStageMain(int argc, char** argv) {
... 省略部分
// 特定のパーティションデバイスをマウントします
if (!DoFirstStageMount(!created_devices)) {
LOG(FATAL) << "必要なパーティションを早期にマウントできませんでした...";
}
... 省略部分
}
SELinux に関連する作業
// first_stage_init.cpp
int FirstStageMain(int argc, char** argv) {
... 省略部分
struct stat new_root_info;
if (stat("/", &new_root_info) != 0) {
PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk";
old_root_dir.reset();
}
if (old_root_dir && old_root_info.st_dev != new_root_info.st_dev) {
FreeRamdisk(old_root_dir.get(), old_root_info.st_dev);
}
// セキュリティフレームワークを初期化します: Android Verified Boot & AVBは、システムファイルの改ざんを防ぐために主に使用されます
// また、システムのロールバック機能を防ぎます
SetInitAvbVersionInRecovery();
setenv(kEnvFirstStageStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(),
1);
... 省略部分
}
execv を介して init プロセスの SecondStageMain メソッドを起動
// first_stage_init.cpp
int FirstStageMain(int argc, char** argv) {
... 省略部分
// initプロセスの引数を設定します
const char* path = "/system/bin/init";
const char* args[] = {path, "selinux_setup", nullptr};
// Binderを開く(ログメッセージ)
auto fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
close(fd);
// 上記の引数を渡します
// initプロセスを起動し、引数は"selinux_setup"です
execv(path, const_cast<char**>(args)); // execvを介してinitプロセスのSecondStageMainメソッドを起動します
}
init プロセスの挿話 SE#
selinux.cpp#SetupSelinux
第二段階の前に SE 設定が行われます。
first_stage_init#FirstStageMain
ファイルのマウント後、execv 関数を介して再度 main.cpp プロセスを起動し、再度main.cpp#main
メソッドに入ります。
// /init/main.cpp ファイル
#include "builtins.h"
#include "first_stage_init.h"
#include "init.h"
#include "selinux.h"
#include "subcontext.h"
#include "ueventd.h"
using namespace android::init;
... 省略部分
// 再度入ると、argvはselinux_setup文字列を持っています
int main(int argc, char** argv) {
... 省略部分
// 後で復元される優先度をブーストします
setpriority(PRIO_PROCESS, 0, -20);
// 引数を判断します
if (!strcmp(basename(argv[0]), "ueventd")) { // ueventdはデバイスノード、権限、匿名共有メモリなどを作成します
return ueventd_main(argc, argv);
}
// 引数の数が> 1の場合
if (argc > 1) {
if (!strcmp(argv[1], "subcontext")) {
// ログを初期化します
android::base::InitLogging(argv, &android::base::KernelLogger);
const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
return SubcontextMain(argc, argv, &function_map);
}
// Selinuxに関連するセキュリティ
if (!strcmp(argv[1], "selinux_setup")) {
return SetupSelinux(argv);
}
// initプロセス(第2段階)
if (!strcmp(argv[1], "second_stage")) {
return SecondStageMain(argc, argv);
}
}
// initプロセスを起動します(第1段階)
return FirstStageMain(argc, argv);
}
SetupSelinux は主に SE ポリシーをロードし、SE が完了した後に新しいパラメータを設定し、再度 execv を介して main.cpp を起動します(前の小節で説明した通り)。
// /init/selinux.cpp
int SetupSelinux(char** argv) {
SetStdioToDevNull(argv);
// 1. コアのログを初期化します
InitKernelLogging(argv);
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
}
boot_clock::time_point start_time = boot_clock::now();
// 2. 欠落しているシステムブロックをマウントします
MountMissingSystemPartitions();
SelinuxSetupKernelLogging();
LOG(INFO) << "SELinuxポリシーを開いています";
PrepareApexSepolicy();
// ポリシーを読み込む前にsnapuserdを殺す可能性があります。
std::string policy;
// ポリシーを読み込みます
ReadPolicy(&policy);
CleanupApexSepolicy();
auto snapuserd_helper = SnapuserdSelinuxHelper::CreateIfNeeded();
if (snapuserd_helper) {
// 監査メッセージを避けるために古いsnapusedを殺します。この後、FinishTransition()を呼び出すまで/system(または他の動的パーティション)から読み取ることはできません。
snapuserd_helper->StartTransition();
}
// SEポリシーをロードします
LoadSelinuxPolicy(policy);
... 省略部分
const char* path = "/system/bin/init";
const char* args[] = {path, "second_stage", nullptr};
// 再度このプロセスを起動します
execv(path, const_cast<char**>(args));
return 1;
}
init プロセス第 2 段階#
init.cpp#SecondStageMain init.rc を解析します。
first_stage_init の初期化マウントデバイスを経て、main.cpp クラスの SecondStageMain 関数が実行されます(init プロセスの第 2 段階と見なすことができます)。
// /init/main.cpp ファイル
#include "builtins.h"
#include "first_stage_init.h"
#include "init.h"
#include "selinux.h"
#include "subcontext.h"
#include "ueventd.h"
using namespace android::init;
... 省略部分
// 再度入ると、argvはsecond_stage文字列を持っています
int main(int argc, char** argv) {
... 省略部分
// 後で復元される優先度をブーストします
setpriority(PRIO_PROCESS, 0, -20);
// 引数を判断します
if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv);
}
if (argc > 1) {
if (!strcmp(argv[1], "subcontext")) {
// ログを初期化します
android::base::InitLogging(argv, &android::base::KernelLogger);
const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
return SubcontextMain(argc, argv, &function_map);
}
// Selinuxに関連するセキュリティ
if (!strcmp(argv[1], "selinux_setup")) {
return SetupSelinux(argv);
}
// initプロセス(第2段階)
if (!strcmp(argv[1], "second_stage")) {
return SecondStageMain(argc, argv);
}
}
// initプロセスを起動します(第1段階段)
return FirstStageMain(argc, argv);
}
init.cpp#SecondStageMain
を分析し、10 の段階に分かれています。詳細はコメントを参照してください。
プロパティ領域を初期化します。主に PropertyInit 関数を実行します。
// init/init.cpp
int SecondStageMain(int argc, char** argv) {
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers(); // initが失敗した場合、Boot Loaderを再起動します
}
// 起動時間
boot_clock::time_point start_time = boot_clock::now();
trigger_shutdown = [](const std::string& command) { shutdown_state.TriggerShutdown(command); };
// 標準入力、標準出力、エラーを空のデバイスファイル `/dev/null` にリダイレクトします
SetStdioToDevNull(argv);
// カーネルログシステムを初期化します
InitKernelLogging(argv);
// システムの$PATHパスを初期化します
if (setenv("PATH", _PATH_DEFPATH, 1) != 0) {
PLOG(FATAL) << "Could not set $PATH to '" << _PATH_DEFPATH << "' in second stage";
}
// initプロセスは他のプロセスに依存しないため、クラッシュしないので、信号を受け取るべきではありません。
// したがって、ここで受信した信号を処理する関数をnullに再設定します。
{
// sigactionは信号です
struct sigaction action = {.sa_flags = SA_RESTART};
action.sa_handler = [](int) {};
// SIGPIPE信号を無視します
sigaction(SIGPIPE, &action, nullptr); // SIGPIPEをnullに設定します
}
// initとそのフォークされた子プロセスのoom_adjを設定します。
if (auto result =
WriteFile("/proc/1/oom_score_adj", StringPrintf("%d", DEFAULT_OOM_SCORE_ADJUST));
!result.ok()) {
LOG(ERROR) << "Unable to write " << DEFAULT_OOM_SCORE_ADJUST
<< " to /proc/1/oom_score_adj: " << result.error();
}
// すべてのプロセスがアクセスできるセッションキーリングを設定します。
// それはFBE暗号化キーなどのものを保持します。どのプロセスもそのセッションキーリングを上書きするべきではありません。
// syscallを呼び出して関連パラメータを設定します
keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 1);
// ブート中であることをバックグラウンドfwローダーなどに示します。
// /dev/.bootingファイルを作成し、ブート中であることをマークします
close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
... 省略部分
PropertyInit(); // init/property_service.cppで実装されています
... 省略部分
return 0;
}
環境変数をクリアします:以前にシステムプロパティに書き込まれた環境変数をクリアします。
// /init/init.cpp
int SecondStageMain(int argc, char** argv) {
... 省略部分
// 以前にシステムプロパティに書き込まれた環境変数をクリアします
// 環境をクリーンアップします。
unsetenv("INIT_AVB_VERSION");
unsetenv("INIT_FORCE_DEBUGGABLE");
... 省略部分
}
SELinux に関連する作業
// /init/init.cpp
int SecondStageMain(int argc, char** argv) {
... 省略部分
// 現在、第二段階のSELinuxを設定します。
SelinuxSetupKernelLogging(); // 第二段階のSELinux作業を完了します
SelabelInitialize(); // 一部のプロセッサを登録します
SelinuxRestoreContext(); // SELinuxが保存したコンテキスト
... 省略部分
}
epoll イベント通知を作成します
// /init/init.cpp
int SecondStageMain(int argc, char** argv) {
... 省略部分
Epoll epoll;
// epoll.Open()は子プロセスのI/Oをリッスンします
if (auto result = epoll.Open(); !result.ok()) {
PLOG(FATAL) << result.error();
}
... 省略部分
}
子プロセスの信号処理器を装載します:init はデーモンプログラム(デーモンスレッドとして理解できます)であり、このプロセスを主として、init プロセスが終了すると、他の子プロセスも終了します。
// /init/init.cpp
int SecondStageMain(int argc, char** argv) {
... 省略部分
// 子プロセスの終了信号を処理するハンドラを作成し、epollに信号を登録します(変更があればepollされます)
// epollは最初に通知を受け取り、その後ソケットでデータを読み取ります
// initプロセスの子プロセスがゾンビプロセスになるのを防ぐため
InstallSignalFdHandler(&epoll);
InstallInitNotifier(&epoll);
... 省略部分
}
プロパティサービスを起動します:主な関数 StartPropertyService がプロパティ変更をリッスンするソケットを起動します。
// /init/init.cpp
static int property_fd = -1;
int SecondStageMain(int argc, char** argv) {
... 省略部分
// 他のプロパティサービスを起動します
StartPropertyService(&property_fd);
unsetenv("INIT_AVB_VERSION"); // 環境変数INIT_AVB_VERSIONを削除します
fs_mgr_vendor_overlay_mount_all();
export_oem_lock_status(); // 最終的に"ro.boot.flash.locked"の値を決定します
MountHandler mount_handler(&epoll);
// USBストレージのUDCコントローラを設定します(sys/class/udc)
SetUsbController();
... 省略部分
}
コマンドと関数の対応関係をマッチングします:Cmd & Function Map。ここからシステムがサポートするコマンド指令と、指令が実際に機能する方法がわかります。
// /init/init.cpp
int SecondStageMain(int argc, char** argv) {
... 省略部分
// Function & Cmdのマップを作成します
const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
// Actionにfunction_mapオブジェクトを保存します
// Action::set_function_map、`::`はstatic Functionに相当します
Action::set_function_map(&function_map);
if (!SetupMountNamespaces()) {
PLOG(FATAL) << "SetupMountNamespacesに失敗しました";
}
... 省略部分
}
LoadBootScripts:init.rc ファイルを解析します。
// /init/init.cpp
int SecondStageMain(int argc, char** argv) {
... 省略部分
// その後、このオブジェクトを使用して解析結果を取得します
ActionManager& am = ActionManager::GetInstance();
ServiceList& sm = ServiceList::GetInstance();
// スクリプトを解析します
LoadBootScripts(am, sm);
... 省略部分
}
// .rcスクリプトを解析します
static void LoadBootScripts(ActionManager& action_manager,
ServiceList& service_list) {
// 解析器を作成します
Parser parser = CreateParser(action_manager, service_list);
// ro.boot.init_rcをデフォルトで解析します
std::string bootscript = GetProperty("ro.boot.init_rc", "");
if (bootscript.empty()) {
// 指定されたinit.rcファイルを解析します
parser.ParseConfig("/system/etc/init/hw/init.rc");
if (!parser.ParseConfig("/system/etc/init")) {
late_import_paths.emplace_back("/system/etc/init");
}
parser.ParseConfig("/system_ext/etc/init");
if (!parser.ParseConfig("/vendor/etc/init")) {
late_import_paths.emplace_back("/vendor/etc/init");
}
if (!parser.ParseConfig("/odm/etc/init")) {
late_import_paths.emplace_back("/odm/etc/init");
}
if (!parser.ParseConfig("/product/etc/init")) {
late_import_paths.emplace_back("/product/etc/init");
}
} else {
parser.ParseConfig(bootscript);
}
}
トリガーイベントを追加します:ここでは rc ファイルのいくつかの設定が見られます。一般的なコマンドには、1 early-init、2 init、3 late-init が含まれます。
// /init/init.cpp
int SecondStageMain(int argc, char** argv) {
... 省略部分
// ActionManagerが解析を完了しました
am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
am.QueueBuiltinAction(TestPerfEventSelinuxAction, "TestPerfEventSelinux");
am.QueueBuiltinAction(ConnectEarlyStageSnapuserdAction, "ConnectEarlyStageSnapuserd");
// early-initラベルをトリガーします
am.QueueEventTrigger("early-init");
// coldbootが完了するのを待つアクションをキューに追加します。これにより、ueventdが/devのすべてをセットアップしたことがわかります...
am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
// ...これにより、/devからの情報を必要とするアクションをキューに追加できます。
am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
Keychords keychords;
am.QueueBuiltinAction(
[&epoll, &keychords](const BuiltinArguments& args) -> Result<void> {
for (const auto& svc : ServiceList::GetInstance()) {
keychords.Register(svc->keycodes());
}
keychords.Start(&epoll, HandleKeychord);
return {};
},
"KeychordInit");
// ブートアクションをすべてトリガーして開始します。
am.QueueEventTrigger("init");
// チャージャーモードではファイルシステムをマウントしたり、コアシステムサービスを開始したりしません。
std::string bootmode = GetProperty("ro.bootmode", "");
if (bootmode == "charger") {
am.QueueEventTrigger("charger");
} else {
am.QueueEventTrigger("late-init");
}
// 現在のプロパティの状態に基づいてすべてのプロパティトリガーを実行します。
am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
... 省略部分
}
ループに入ります:受信したコマンドを処理し続け、サービスを実行します。
// /init/init.cpp
int SecondStageMain(int argc, char** argv) {
... 省略部分
while(true) {
// デフォルトでは、何かが起こるまでスリープします。
auto epoll_timeout = std::optional<std::chrono::milliseconds>{};
// シャットダウンコマンドを受信したかどうかを確認します
auto shutdown_command = shutdown_state.CheckShutdown();
if (shutdown_command) {
HandlePowerctlMessage(*shutdown_command);
shutdown_state.set_do_shutdown(false);
}
... 省略部分
// 待機する必要がなく、サービスが実行中でないか、サービスが完了している場合
if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
am.ExecuteOneCommand(); // 1つのコマンドを実行します
}
... 省略部分
}
}