メインコンテンツへスキップ
  1. ノート/
  2. システム底层/

Linuxの起動フロー:ファームウェア、パーティション、GRUBからカーネルまで

ICE345
著者
ICE345
CS Student | System | Linux | OCaml
目次

このページでは、混同されやすい ファームウェア、起動モード、パーティション方式、ブートマネージャー、ブートローダー、ELF、Linuxカーネル、ユーザー空間 を、電源投入後の流れに沿って整理します。

まず全体像を示します。起動処理は順番のある段階的な処理なので、Blowfishの timeline shortcode で表すと理解しやすくなります。

  1. ハードウェアのリセットとファームウェア

    1

    BIOS / UEFI

    CPUはリセットベクターから実行を開始し、ファームウェアがプロセッサー、メモリ、バス、必要な起動デバイスを初期化します。
  2. ファームウェアが起動項目を選択

    2

    Boot Manager

    Legacy BIOSは通常ディスクの先頭セクターからコードを実行します。UEFIは通常、起動項目に従ってESPからEFIプログラムを読み込みます。
  3. GRUBなどのブートプログラムへ移行

    3

    Bootloader

    ブートプログラムが設定を読み込み、メニューを表示し、起動対象のOSまたはカーネルを準備します。
  4. Linuxカーネルが実行を開始

    4

    Kernel + initramfs

    GRUBがLinuxカーネル、initramfs、カーネルコマンドラインを起動プロトコルに従って渡し、制御をカーネルへ移します。
  5. カーネルがシステムを初期化

    5

    Memory / Drivers / Root FS

    Linuxがメモリ、プロセス、ドライバー、ファイルシステムを初期化し、initramfsを使って本来のルートファイルシステムを見つけてマウントします。
  6. PID 1とユーザー空間

    6

    systemdまたは別のinit

    PID 1がサービス、ログイン画面、デスクトップ、通常のアプリケーションを起動し、システムが通常運用の状態に入ります。

重要なのは、起動モードとパーティション方式は別の軸であり、ELFはファイル形式、GRUBはブートプログラム、カーネルは起動されるOSの中核だということです。「UEFIは必ずGPT」「.efiは必ずELF」「カーネルは普通のELFプログラム」と考えると、実際の起動処理を正確に説明できなくなります。

1. まず4つの概念を分ける
#

1.1 起動モード:Legacy BIOSとUEFI
#

起動モードは、コンピューターの電源投入後、マザーボードのファームウェアがどのように起動プログラムを探して実行するかを表します。

  • Legacy BIOSは、従来のPCで使われてきた起動インターフェースです。通常はディスクの第1セクターを読み込み、決められたメモリ領域に置いて実行を開始します。
  • UEFIは、より新しいファームウェアインターフェースです。FATファイルシステムにアクセスし、EFIシステムパーティションにある起動ファイルを読み込み、起動項目から実行するEFIアプリケーションを選択できます。

1.2 パーティション方式:MBRとGPT
#

パーティション方式は、ディスク上のパーティション配置をどのように記録するかを表します。

  • MBRパーティション方式は従来型のパーティションテーブルを使い、パーティション数とディスクアドレスの範囲に制限があります。
  • GPTは各パーティションにGUIDを割り当て、ディスクの先頭と末尾にパーティションテーブルと検査情報を保持します。大容量ディスクや現代的な構成に適しています。

MBRとGPTは起動モードでも、ブートローダーでもありません。代表的な組み合わせは次のとおりです。

起動モードパーティション方式説明
Legacy BIOSMBR従来の組み合わせ。BIOSがディスク先頭セクターのコードを実行する
UEFIGPT現代のPCで一般的な組み合わせ。UEFIがESPから.efiファイルを読む
Legacy BIOSGPT可能だが、GRUBには通常BIOS Boot Partitionが必要
UEFIMBR一部の環境では可能だが、現代のLinuxインストールで第一候補になる構成ではない

したがって、「UEFI + GPT」と「BIOS + MBR」はよく使われる組み合わせではありますが、完全に一対一で対応するわけではありません。

1.3 ブートマネージャーとブートローダー
#

  • **ブートマネージャー(boot manager)**は、どのシステム、カーネル、起動項目を選ぶかを担当します。
  • **ブートローダー(bootloader)**は、選択されたOSのカーネルまたは次の起動プログラムをメモリに読み込み、制御を渡します。

この2つの役割を同じプログラムが担うこともよくあります。GRUBは代表的な例です。メニューを表示するのでブートマネージャーとして動作し、Linuxカーネルとinitramfsを読み込むのでブートローダーとしても動作します。

UEFIファームウェア自体にもブートマネージャーの機能があります。NVRAMに保存された BootOrderBoot#### の起動項目に従って、GRUB、Windows Boot Manager、systemd-bootなどのEFIアプリケーションを選択して実行します。

2. ELFとは何か
#

ELFは Executable and Linkable Format の略で、「実行可能・リンク可能フォーマット」を意味します。Linux/Unixのツールチェーンでは、ELFは次のようなファイルに使われます。

  • 実行可能ファイル
  • libc.so などの共有ライブラリ
  • コンパイラーが生成する.o再配置可能オブジェクトファイル
  • core dump
  • 通常のLinuxカーネルモジュール.ko

ただし、LinuxのすべてのバイナリがELFというわけではありません。たとえば、UEFIの.efi起動プログラムは通常PE/COFF形式です。また、LinuxのvmlinuzはLinux固有の起動プロトコル情報を含む圧縮カーネルイメージであり、一般的なELF実行ファイルと単純に同一視できません。ファームウェア、デバイス用マイクロコード、ベアメタル用イメージなども、別の形式を使うことがあります。

2.1 ELFファイルの構造
#

ELF Header
  ├── Program Header Table(プログラムヘッダーテーブル。実行時に重要)
  ├── Section Header Table(セクションヘッダーテーブル。リンクや解析に利用)
  └── コード、データ、文字列、その他のメタデータ

ELF Headerには、次のような基本情報が記録されています。

  • 32ビットか64ビットか
  • x86-64やAArch64などの対象アーキテクチャー
  • 実行可能ファイル、共有オブジェクト、再配置可能ファイルなどの種別
  • プログラムのエントリーポイントe_entry
  • プログラムヘッダーテーブルとセクションヘッダーテーブルの位置

e_entryはエントリーアドレスですが、「ソースコードの1行目」を意味するわけではありません。動的リンクされたプログラムでは、実行時のスタートアップコードや動的ローダーを経由してから、最終的にプログラムの初期化処理へ進みます。

2.2 SectionとSegmentは別物
#

ELFで最も混同されやすいのが、この2つです。

**Section(セクション)**は、コンパイル、リンク、デバッグを主な目的とします。

  • .text:機械語命令
  • .rodata:読み取り専用データや文字列定数
  • .data:初期値を持つ書き込み可能なデータ
  • .bss:未初期化、またはゼロで初期化されるデータ。通常、ファイルに全内容を保存する必要はない
  • .symtab:シンボルテーブル。配布用バイナリでは削除されることがある
  • .debug_*:デバッグ情報。軽量化した配布版には含まれないことが多い

**Segment(セグメント)**は、プログラムのロードを主な目的とします。OSのローダーはProgram Header TableにあるPT_LOADなどのエントリーを参照し、ファイルの適切な範囲をプロセスの仮想アドレス空間にマッピングして、読み取り・書き込み・実行の権限を設定します。

つまり、より正確には次のように説明できます。

リンカーが複数のSectionをいくつかのSegmentにまとめ、実行時ローダーは主にSegment単位でプログラムをロードする。.text.dataをそれぞれ独立したファイルブロックとして単純にコピーするわけではない。

readelf -h ./program       # ELF全体の情報
readelf -S ./program       # Sectionの一覧
readelf -l ./program       # Program HeaderとSegment
readelf -d ./program       # 動的リンク情報
file ./program             # ファイル形式の簡易判定

2.3 LinuxがELFプログラムを起動する流れ
#

動的リンクされた通常のプログラムを例にすると、流れは次のようになります。

シェルが ./program を実行
execve() がカーネルにプロセスイメージの置き換えを要求
カーネルがELF HeaderとProgram Headerを認識
ロード可能なPT_LOAD Segmentをプロセスへマッピング
PT_INTERPがあれば動的ローダーを特定
動的ローダーが共有ライブラリを読み込み、再配置を実行
プログラムのエントリーポイントとCランタイムの初期化処理へ進む
main()が呼び出される

ここでいう「マッピング」は、ファイル全体を一度にRAMへコピーすることではありません。ファイルマッピング、オンデマンドページング、ページキャッシュを利用し、実際にページへアクセスした時点で必要なデータを読み込むこともあります。

2.4 .SアセンブリソースとELFの関係
#

.Sはアセンブリソースでよく使われる拡張子です。大文字の.Sは通常Cプリプロセッサーを先に通し、小文字の.sは通常そのままアセンブラーへ渡します。どちらもソースコードであり、ELFファイルそのものではありません。

典型的なビルドの流れは次のとおりです。

boot.S / head.S
      ↓ プリプロセッサー + アセンブラー
foo.o(通常はELF再配置可能オブジェクト)
      ↓ リンカーとリンカースクリプト
実行可能ファイル、カーネルイメージ、共有ライブラリなど

アセンブリソースの次の記述は、アセンブラーに入力Sectionの生成方法を伝えます。

.section .text
.global _start
_start:
    nop

.section .data
value:
    .long 0x1234

その後、リンカーがリンカースクリプトに従って各Sectionの出力ファイル内での配置、仮想アドレス、Segmentへの所属を決めます。.S.textと最終的なELFの.textには関係がありますが、両者は「同じファイル構造のテキスト版とバイナリ版」ではありません。

3. UEFIモードの起動フロー
#

ここでは、現代的なx86-64 LinuxのUEFI + GPT構成を例にします。アーキテクチャーによってカーネルの起動プロトコルは異なりますが、全体の階層はおおむね同じです。

3.1 CPUがリセットされ、ファームウェアが実行される
#

電源投入またはリセットの後、CPUはアーキテクチャーで定められたリセットベクターからマザーボードのファームウェアを実行します。ファームウェアは基本的なハードウェアを初期化し、メモリ、プロセッサー、表示装置、ストレージなどをPOSTで確認します。

UEFIは完全なデスクトップOSではありません。OSが起動する前に動くファームウェア環境であり、デバイスアクセス、ファイルシステムアクセス、起動サービス、EFIプログラムの実行に必要なインターフェースを提供します。

3.2 UEFIのブートマネージャーが起動項目を選ぶ
#

UEFIは通常、ファームウェア変数に保存された起動項目を参照します。起動項目は、次のようなEFIファイルを指します。

\EFI\Microsoft\Boot\bootmgfw.efi
\EFI\ubuntu\grubx64.efi
\EFI\Linux\linuxx64.efi

起動項目が見つからない場合、UEFIは環境によってリムーバブルメディア用の既定パスも試します。x86-64では、次のパスがよく使われます。

\EFI\BOOT\BOOTX64.EFI

3.3 UEFIがESPのEFIプログラムを読む
#

ESPは EFI System Partition の略です。通常はFATファイルシステムを使用し、EFI起動プログラムと関連ファイルを保存します。ESPは1つのパーティションであり、/boot全体でも、GPTそのものでもありません。

ここで重要な点を1つ訂正しておきます。

.efiファイルは通常、UEFIが使うPE/COFF形式のイメージであり、ELFではありません。

UEFI仕様ではPE32/PE32+形式のEFIイメージが定義されています。そのため、GRUBのgrubx64.efiはUEFI用の実行プログラムであり、「ELFに.efiという拡張子を付けたもの」と理解するのは正確ではありません。

3.4 GRUBが制御を引き継ぐ
#

UEFIがgrubx64.efiを実行すると、GRUBは次のようなファイルを読み込むことがあります。

  • grub.cfg
  • GRUBモジュール
  • フォント、テーマ、ローカライズファイル
  • /bootにあるLinuxカーネルとinitramfs

複数のOSやカーネルがインストールされている場合、GRUBはメニューを表示できます。ユーザーが項目を選ぶと、GRUBは通常、Linuxカーネルイメージ、initramfs、カーネルコマンドライン(root=quiet、ハードウェア関連のパラメーターなど)を準備します。

initramfsは、実際のルートファイルシステムをマウントする前に使われる一時的なルートファイルシステムです。ストレージドライバーの読み込み、LUKSの解除、RAID/LVMの組み立て、デバイスの検出、ルートパーティションの特定などに利用されます。

4. Legacy BIOSモードの起動フロー
#

ここではBIOS + MBR構成を例にします。

4.1 BIOSがディスク先頭セクターを読む
#

BIOSはPOSTと起動デバイスの選択を終えると、起動ディスクの第1セクターを読み込みます。従来のBIOS起動では、通常次の処理が行われます。

  • セクターを物理アドレス0x7C00付近へ読み込む
  • 末尾の起動シグネチャ0x55AAを確認する
  • そのセクター内の起動コードへCPUの制御を移す

4.2 MBRの512バイト構造
#

従来のMBR起動セクターは、概ね次のように分けられます。

オフセット 0   - 445:起動コード領域。一般に446バイトと説明される
オフセット 446 - 509:4つの16バイトのパーティションエントリー。合計64バイト
オフセット 510 - 511:起動シグネチャ0x55AA。合計2バイト

次の3点には注意が必要です。

  1. MBRは、ディスク先頭セクター全体を指す場合と、その中のパーティションテーブルや起動コードを指す場合があります。
  2. パーティションテーブルはディスクのレイアウトを記録するだけで、単独でOSを読み込むことはありません。
  3. 「MBRに起動コードがない」という状態では、従来のBIOS起動経路は続行できません。ただし、UEFIがESPから起動する経路まで必ず失敗するわけではありません。

4.3 BIOSモードでのGRUBの埋め込み
#

従来の教材では、GRUBをStage 1、Stage 1.5、Stage 2に分けて説明することがあります。

boot.img       → ディスク起動セクター内の最小コード
core.img       → 埋め込み領域に置かれるコアイメージ
モジュールと設定 → ファイルシステム内の/boot/grubなど

「Stage 1.5」は歴史的な流れを理解するには便利ですが、すべてのGRUBバージョンと起動方式に存在する固定ファイルではありません。現在のGRUB 2を説明する場合は、boot.imgcore.img、モジュール、設定ファイルという用語を使う方が適切です。

MBRディスクでは、GRUBのコアイメージは通常、第1パーティションより前の空き領域へ埋め込まれます。この領域はMBR gapやembedding areaと呼ばれることがありますが、正式なファイルシステムパーティションではなく、常に安全に利用できるとも限りません。現在のパーティションツールは、アライメントのため第1パーティションを約1MiB地点から開始することが多く、ブートローダー用の空間も確保しやすくなっています。

5. GPT、ESP、BIOS Boot Partition
#

5.1 GPTの基本レイアウト
#

GPTディスクは通常、次のような構造を持ちます。

LBA 0:Protective MBR(保護MBR)
LBA 1:プライマリGPT Header
後続:プライマリGPTパーティションエントリー配列
中央:実際のパーティション領域
ディスク末尾:バックアップのエントリー配列とGPT Header

Protective MBRの主な目的は、MBRしか理解できない古いツールがGPTディスクを「パーティションのない空のディスク」と誤認して上書きするのを防ぐことです。これは従来のBIOS起動用コード一式を意味するものではありません。

GPTのプライマリテーブルとバックアップテーブルには検査情報があります。ディスク先頭のGPT Headerには、パーティションエントリー配列の位置、数、サイズなどが記録され、バックアップGPT Headerはディスク末尾に置かれます。

5.2 GPT + UEFI
#

これは現代のPCで最も一般的な組み合わせです。

UEFI
GPTディスク上のESP
ESP内のgrubx64.efiなどのEFIプログラム
カーネル、initramfs、ユーザー空間

GPTが直接GRUBを保存するわけではありません。GPTがパーティションの配置を提供し、その中のESPがFATファイルシステムを使い、EFI起動プログラムをファイルとして保存します。

5.3 GPT + BIOS
#

GPTディスクでも、従来のBIOSモードでGRUBとLinuxを起動できます。GPTのレイアウトはMBR後の空き領域に依存しないため、通常は専用の BIOS Boot Partition を作成します。

  • ファイルシステムは必要ない
  • 通常のユーザーファイルを保存するための領域ではない
  • パーティションタイプをBIOS boot用に設定する
  • BIOSモードのGRUBコアイメージを埋め込むために使う

このパーティションはESPでも/bootでもありません。BIOSモードでGRUBを埋め込むためだけに使われます。

6. Linuxカーネルへ制御が移るまで
#

GRUBがカーネル、initramfs、カーネルコマンドラインを準備すると、アーキテクチャー固有の起動プロトコルに従ってカーネルのエントリーポイントへジャンプします。

x86 Linuxでは、/boot/vmlinuz-*は通常、圧縮され、Linuxの起動プロトコル用の構造を含むカーネルイメージです。一方、Linuxのビルドディレクトリにあるvmlinuxは、リンク結果に近い、一般にELF形式のカーネルファイルです。両者は単純に置き換えられません。

vmlinux       → ビルド時に生成される、一般にELF形式のカーネルファイル
vmlinuz       → /bootにインストールされ、ブートローダーから使われる圧縮・起動用イメージ
initramfs     → 起動初期に使われる一時的なルートファイルシステム

カーネルは起動後、おおむね次の処理を進めます。

  1. メモリ管理と例外処理の仕組みを構築する。
  2. スケジューラー、時刻管理、割り込みを初期化する。
  3. カーネルのサブシステムとデバイスモデルを初期化する。
  4. ストレージコントローラー、ファイルシステム、ネットワークなどのドライバーを初期化する。
  5. initramfsを展開して利用する。
  6. 本来のルートファイルシステムを見つけてマウントする。
  7. switch_rootなどを使ってinitramfsから移行する。
  8. PID 1を起動する。

Linuxカーネルがすべてのハードウェアをファームウェアに任せるわけではありません。ファームウェアとカーネルの役割は次のように分かれます。

  • ファームウェアはOSより前にプラットフォームを初期化し、起動環境を提供する。
  • カーネルドライバーは、OSの実行中にハードウェアを制御するソフトウェアインターフェースである。
  • 一部のデバイスは専用のデバイスファームウェアを必要とし、ドライバーがシステム上のファームウェアを読み込んでデバイスへ渡す。
  • Linuxのデバイスファームウェアは、一般に/lib/firmwareに置かれ、usr-mergeを使うディストリビューションでは/usr/lib/firmwareの場合もある。

6.1 ユーザー空間の起動
#

PID 1はsystemd、OpenRC、BusyBox initなどのinit実装です。systemdを例にすると、次の処理が続きます。

  • 基本サービスを起動する
  • その他のファイルシステムをマウントする
  • ネットワークを初期化する
  • ログ、時刻同期、デバイス管理サービスを起動する
  • ディスプレイマネージャーまたはテキストログインサービスを起動する

ここで初めて、一般にいうログイン画面やデスクトップへ進みます。したがって「起動完了」は、カーネルが実行を始めた時点ではなく、ユーザー空間が段階的に整った後の状態です。

7. 起動チェーンにおける役割の関係
#

UEFI環境は次のように考えられます。

UEFIファームウェアのBoot Manager
        │ EFIアプリケーションを選択
grubx64.efi / systemd-boot / Windows Boot Manager
        │ カーネルまたは次の起動プログラムを選択・ロード
Linuxカーネル + initramfs
        │ OSを初期化
PID 1とユーザー空間

BIOS環境は次のようになります。

BIOS
  │ ディスク先頭セクターを読み込んで実行
MBR内のboot.img
  │ core.imgをロード
GRUBのコア、モジュール、設定
  │ カーネルとinitramfsをロード
Linuxカーネル

ただし、これらを常に「ファームウェア → ブートマネージャー → ブートローダー → カーネル」という4つの独立したプログラムとして考える必要はありません。実際には次のような構成もあります。

  • UEFIがGRUBを選び、GRUBがブートマネージャーとカーネルローダーを兼ねる。
  • UEFIがLinux用のカーネルローダーを直接選び、GRUBを使わない。
  • GRUBがchainloadingによってWindows Boot Managerへ制御を渡す。
  • 1つのブートマネージャーが、カーネルではなく別の起動プログラムを選ぶ。

8. 起動済みLinuxで状態を確認する
#

8.1 UEFIかLegacy BIOSかを判定する
#

if [ -d /sys/firmware/efi ]; then
    echo UEFI
else
    echo Legacy-BIOS-or-unknown
fi

これは起動済みのLinuxで実行します。/sys/firmware/efiが存在する場合、通常はLinuxカーネルがUEFI経由で起動したことを示します。

8.2 パーティション方式とファイルシステムを確認する
#

lsblk -o NAME,SIZE,TYPE,FSTYPE,PARTTYPE,PARTLABEL,MOUNTPOINTS
sudo fdisk -l
sudo gdisk -l /dev/nvme0n1

次の点を確認します。

  • vfatのESPが存在するか
  • BIOS Boot Partitionが存在するか
  • ルートファイルシステム、/boot、ESPがどのパーティションにあるか
  • ディスクがGPTかDOS/MBRパーティションテーブルか

8.3 UEFIの起動項目を確認する
#

sudo efibootmgr -v

8.4 カーネル、initramfs、カーネルコマンドラインを確認する
#

ls -lh /boot/vmlinuz* /boot/initramfs* 2>/dev/null
cat /proc/cmdline
ps -p 1 -o pid,comm,args

8.5 ELFを観察する
#

readelf -h /bin/ls
readelf -l /bin/ls
readelf -S /bin/ls

これらのコマンドで、ELF Header、Program Header/Segment、Sectionをそれぞれ確認できます。

9. よくある説明を正確に言い換える
#

誤解しやすい説明より正確な説明
UEFIは小さなOSであるUEFIはファームウェアインターフェースと実行環境であり、完全なOSとは異なる
UEFIは必ずGPTを使うUEFIはGPTと組み合わせることが多いが、起動モードとパーティション方式は別の概念である
GPTにMBRはないGPTディスクには通常、互換性と保護のためのProtective MBRがある。ただし従来のMBR起動方式とは異なる
GPTはBIOSで起動できないGPTはBIOSでも起動でき、GRUBには通常BIOS Boot Partitionが必要になる
.efiはELFに拡張子を付けたものEFIイメージは通常PE/COFF形式である
ELFの.text.dataが、そのままロード時のメモリセグメントになるELFのSectionとSegmentは別物で、実行時は主にProgram HeaderのSegment単位でロードされる
Linuxカーネルは普通のELFプログラムであるvmlinuxは一般にELFだが、vmlinuzは起動プロトコルに対応したカーネルイメージである
Stage 1.5は常にGRUBの固定ファイルであるこれは従来のGRUB起動過程を説明する教育的な呼び方であり、現代のGRUB 2ではboot.imgcore.img、モジュールで説明する方が適切である
MBRにコードがなければ、すべての起動方式が失敗する失敗するのは主に、ディスク先頭から起動する従来のBIOS経路であり、UEFIがESPを使う経路まで必ず失敗するわけではない
ファームウェアとドライバーは同じものであるファームウェアはプラットフォームやデバイス上で動き、ドライバーはカーネルがデバイスを制御するためのソフトウェアである

参考資料
#

関連記事


评论