カーネル起動と Init プロセスの生成
Android システムの起動は、ブートローダーによってカーネルがメモリ上にロードされることから始まります。カーネル起動後、最初に生成されるのは PID 0 の Idle プロセス(swapper とも呼ばれる)です。このプロセスはメモリ管理などのカーネル内部初期化を担当し、続いて init プロセスと kthread を生成します。kthread はカーネルスレッドを管理し、ユーザー空間のプロセスは init によって起動されます。
カーネルソースの init/main.c にある kernel_init 関数において、init プロセスの実行パスが決定されます。以下のコードは、指定されたコマンドまたは標準的なパスから init バイナリを実行する逻辑を示しています。
static int __init kernel_init(void *unused)
{
int status;
const char *init_command = boot_command_line; // 例:コマンドラインから取得
if (init_command) {
status = attempt_init_execution(init_command);
if (status == 0)
return 0;
panic("Requested init %s failed (error %d).",
init_command, status);
}
// 標準的な init パスを試行
const char *init_paths[] = {
"/sbin/init",
"/etc/init",
"/bin/init",
"/bin/sh"
};
for (int i = 0; i < sizeof(init_paths)/sizeof(init_paths[0]); i++) {
if (attempt_init_execution(init_paths[i]) == 0)
return 0;
}
panic("No working init found.");
return 0;
}
static int attempt_init_execution(const char *init_path)
{
const char *argv[] = { init_path, NULL };
pr_info("Executing init process: %s\n", init_path);
// 用户空間プログラムの実行を行うカーネル関数
return do_execve(getname_kernel(init_path),
(const char __user *const __user *)argv,
(const char __user *const __user *)envp_init);
}
do_execve はユーザー空間のプログラムを実行するためのカーネル関数であり、内部で __do_execve_file を呼び出します。ここでは、ファイルディスクリプタの処理、プロセス数の制限チェック、メモリ空間の準備、および引数や環境変数のコピーが行われた後、実際にバイナリが実行されます。
Init プロセスの起動ステージ
カーネルから制御が渡されると、system/core/init/main.cpp の main 関数が実行されます。init プロセスは起動引数に応じて異なるステージに分かれて動作します。
int main(int arg_count, char** arg_vector) {
// サンイタイザー設定(デバッグ用)
#if __has_feature(address_sanitizer)
__asan_set_error_report_callback(AsanReportCallback);
#endif
std::string process_name = basename(arg_vector[0]);
if (process_name == "ueventd") {
return ueventd_main(arg_count, arg_vector);
}
if (arg_count > 1) {
std::string mode = arg_vector[1];
if (mode == "subcontext") {
android::base::InitLogging(arg_vector, &android::base::KernelLogger);
const BuiltinFunctionMap& func_map = GetBuiltinFunctionMap();
return SubcontextMain(arg_count, arg_vector, &func_map);
}
if (mode == "selinux_setup") {
return SetupSelinux(arg_vector);
}
if (mode == "second_stage") {
return SecondStageMain(arg_count, arg_vector);
}
}
// デフォルトは第一段階
return FirstStageMain(arg_count, arg_vector);
}
第一段階(FirstStageMain)では、ファイルシステムのマウント、デバイスの作成、ログシステムの初期化などが行われます。その後、SELinux の設定を行い、第二段階(SecondStageMain)へ遷移します。
第二段階と init.rc の解析
第二段階では、プロパティサービスの開始や SELinux のコンテキスト復元などが実行されます。重要な処理として、init.rc スクリプトの解析があります。
static void LoadBootScripts(ActionManager& action_mgr, ServiceList& svc_list) {
Parser parser = CreateParser(action_mgr, svc_list);
std::string bootscript = GetProperty("ro.boot.init_rc", "");
if (bootscript.empty()) {
parser.ParseConfig("/system/etc/init/hw/init.rc");
// 各パーティションの init スクリプトを順次解析
parser.ParseConfig("/system/etc/init");
parser.ParseConfig("/system_ext/etc/init");
parser.ParseConfig("/product/etc/init");
parser.ParseConfig("/odm/etc/init");
parser.ParseConfig("/vendor/etc/init");
} else {
parser.ParseConfig(bootscript);
}
}
パーサーは "service"、"on"、"import" などのセクションを解釈し、サービス定義とアクションを登録します。例えば、Zygote の起動は init.rc 内のトリガーによって制御されます。
# 暗号化状態に基づいて Zygote を起動
trigger zygote-start
この際、import /system/etc/init/hw/init.${ro.zygote}.rc のように、システムプロパティ ro.zygote の値に応じて具体的な RC ファイルが動的に読み込まれます。これにより、32 ビット環境、64 ビット環境、またはその組み合わせに応じて適切な Zygote 構成が適用されます。
Zygote プロセスの起動と Java 環境
Zygote は Android ランタイム環境の中核であり、すべてのアプリプロセスの親となります。init によって fork された Zygote は、ネイティブ層から Java 層へ遷移します。
ネイティブ側では AppRuntime クラスが AndroidRuntime を継承しており、runtime.start メソッドによって VM が起動されます。
- VM 起動:
startVmで Java 仮想機械を生成(ヒープサイズ初期化など)。 - JNI 登録:
startRegでネイティブ関数を Java から呼び可能に注册。 - Java エントリー:
ZygoteInit.mainを呼び出し。
Java 層に入った Zygote は、クラスやリソースのプリロードを行い、その後ソケットサーバーとして動作し、SystemServer の fork を待ち受けます。
// ZygoteInit.java の主要フロー
public static void main(String argv[]) {
// リソースの事前読み込み
preload(bootTimingsTraceLog);
// ソケットサーバーの作成
zygoteServer = new ZygoteServer(isPrimaryZygote);
// SystemServer プロセスの fork
Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);
if (r != null) {
r.run(); // SystemServer として実行
return;
}
// Zygote はループしてアプリ起動要求を待つ
caller = zygoteServer.runSelectLoop(abiList);
}
SystemServer とサービス管理
Zygote によって fork された SystemServer プロセスは、AMS(Activity Manager Service)や WMS(Window Manager Service)など、主要なシステムサービスホストします。サービスの起動は SystemServiceManager によって統一的に管理されます。
// SystemServer.java 内のサービス起動例
mSystemServiceManager.startService(ActivityManagerService.Lifecycle.class);
SystemServiceManager は、SystemService を継承したクラスを引数に取ります。AMS 本体は IActivityManager.Stub を継承していますが、システムサービスとしてのライフサイクル管理を行うために、静的内部クラス Lifecycle が用意されています。
public static final class Lifecycle extends SystemService {
private final ActivityManagerService mService;
public Lifecycle(Context context) {
super(context);
mService = new ActivityManagerService(context);
}
@Override
public void onStart() {
mService.start();
}
public ActivityManagerService getService() {
return mService;
}
}
startService メソッドは反射を用いて Lifecycle クラスのインスタンスを生成し、onStart を呼び出すことでサービスを実際に起動します。起動されたサービスは、ServiceManager.addService を介して Binder サービスとして登録され、他のプロセスから利用可能になります。
ServiceManager.addService(Context.ACTIVITY_SERVICE, mService, true, dumpFlags);
一部のサービスは SystemServiceManager を経由せず、直接 main メソッドなどから起動されて ServiceManager に登録される場合もあります。すべての主要サービスが起動し終わると、systemReady メソッドが呼び出され、システムが利用可能な状態になったことが通知されます。