Linux kernelが起動する際の流れを適当に解説する。
多くの場合は意識されることが少ないが、カーネルといえども通常のCプログラムにおけるmainのようなエントリポイントが存在しておりここからすべての初期化やオペレーティングシステム上のプロセス起動が行われている。その起動シーケンスをカーネルのソースコードレベルで追っていく。
Linux kernelは通常ブートローダによって起動される最も最初のプログラムとなっており、そのプログラムはvmlinuxやImage、また圧縮の有無によってvmlinuzやbzImageなどと命名されている。これらのファイルに加えてデバイスツリー(dtb)、ラムディスク(initrd)、ルートファイルシステム(rootfs)などもブートローダによって取り扱われるがここではvmlinuxカーネル本体にのみ着目する。
仮にアーキテクチャをaarch64であるとして進める。大まかには他のアーキテクチャにおいても同じだろう。Linuxカーネルはいくつかのサブシステムから構成され、その様相はある程度ソースツリーの構成やフォルダ命名から読み取ることができる。kernelコア部分は予想できる通り、
- kernel/
- arch/arm64/kernel
に配置されている。起動を含めてアーキテクチャ固有のインストラクションの呼び出しが必要な部分はarch/arch_name/kernelに.S拡張子のアセンブラプログラムとしてkernelコア部分が置かれている。mainにあたるエントリポイントとして最初に見るべきファイルは下記
- arch/arm64/kernel/vmlinux.lds.S
.Sはアセンブラプログラムを表す拡張子だが、このファイルはアセンブラプログラムではなくリンカスクリプトである。ENTRY(_text)
とあり、続くSETIONS内の.head.textセクションでの指定からvmlinuxプログラムのエントリポイントは.head.textセクションの頭となるようにプログラムがリンクされる。次に見るファイルは
- arch/arm64/kernel/head.S
ここで__HEADというマクロによって.section ".head.text"が宣言されており、ここからプログラムの実行は開始される。ここからの実行パスを追うのはアセンブラといえども難しくないだろう。最終的に実行シーケンスは別ファイルのstart_kernel関数に移る。
- init/main.c
main関数ではないがLinuxのアーキテクチャに依存しないエントリポイントはinit/main.cというわかりやすいファイル名書かれている。このファイル内にstart_kernel関数が定義されており、あとはC言語による初期化シーケンスが続いていくことが読み取れる。最後はrest_init関数からkernel_initの関数ポインタを経由してその後の初期化はタスクスケジューラを動作させながら行われるようだ。
kernel_initを見てみるとkernelの初期化は非常にわかりやすい。最初のプロセスを起動しているのだ。指定されたinitプロセスが起動できなければ/sbin/initを決め打ちで起動しようとするなどのフォールバック実装が見て取れる。