BIOSとUEFI:ファームウェアによる起動の開始

電源を入れた直後、まずマザーボード上のファームウェアであるBIOS(Basic Input/Output System)またはUEFI(Unified Extensible Firmware Interface)が動作します。ファームウェアは、画面やキーボードなどの基本的なハードウェアを初期化し、メモリなどの自己診断(POST、Power-On Self-Test)を行った後、あらかじめ設定された起動デバイスからブートローダを読み込んで実行を引き継ぎますUEFIはBIOSより新しい規格で、多くの現行PC・サーバーで採用されています。UEFI環境でも、互換性のためにBIOS相当の起動方式(レガシーBIOSモード)に切り替えられる機種があります。[1]。

画像

BIOSとUEFIでは、ブートローダを見つけて読み込む方法が異なります。

項目BIOSUEFI
一般的なパーティション形式MBR(GPTからの起動も構成によって可能)GPT(MBRに対応する場合もある)
ブートローダの置き場所ディスク先頭のMBRにあるブートコード領域EFIシステムパーティション内のファイル
起動方法小さな一次ブートローダからGRUB 2などの後続部分を読み込むファームウェアがブートローダファイルを直接読み込む
ディスク容量MBRでは一般に約2TBまでGPTでは2TBを超える大容量ディスクを扱える

MBRはパーティションの位置や大きさを表す情報に制約があるため、一般的な512バイトセクターでは利用できるディスク容量が約2TiB(一般に約2TBと表現)までに制限されます。

これに対してGPTは、より広い範囲を表せるため、2TBを超える大容量ディスクを利用できます。

ただし、約2TBという制限はBIOS自体ではなく、主にMBR形式の制限です。BIOSとGPT、またはUEFIとMBRを組み合わせられる場合もありますが、一般にはBIOSではMBR、UEFIではGPTが使用されます。

このように、BIOSではブートローダを段階的に読み込むのに対し、UEFIではEFIシステムパーティション上のファイルを直接実行します。UEFIとGPTの組み合わせは、起動処理を扱いやすくし、大容量ディスクにも対応できます。

ブートローダ:カーネルへの命令とオプションの受け渡し

ブートローダは、ファームウェアから実行を引き継ぎ、ディスク上からLinuxカーネルと後述のinitramfsをメモリへ読み込んで、カーネルに実行を引き渡すプログラムです。現在のLinuxディストリビューションで最も広く使われているブートローダはGRUB 2(GRand Unified Bootloader version 2)です出題範囲の用語一覧には「bootloader」という一般的な語のみが挙げられており、特定の製品名は明記されていません。本記事では、実務・学習の両面で広く使われているGRUB 2を具体例として説明します。[2]。

起動時にブートローダへ共通のコマンドを与える

GRUB 2が表示する起動メニューでは、通常、キーボード操作によっていくつかの操作ができます。

操作一般的な効果
上下矢印キー起動するメニュー項目(カーネルのバージョン等)を選択する
e選択中のメニュー項目を一時的に編集する(カーネル行にオプションを追記する等)
cGRUBのコマンドラインを開く(lsやset等、GRUB独自のコマンドを直接実行できる)
Ctrl+x(またはF10)編集内容を反映した状態で起動する
Esc編集を中断してメニューに戻る

eキーで編集した内容は、その回の起動限りの一時的な変更です。恒久的に変更したい場合は、/etc/default/grubファイルを編集した後、設定を再生成するコマンド(Debian系ではupdate-grub、Red Hat系ではgrub2-mkconfig)を実行する必要がありますupdate-grubは多くのDebian系ディストリビューションでgrub-mkconfig -o /boot/grub/grub.cfgを呼び出す簡易コマンドとして提供されています。Red Hat系でのgrub2-mkconfigの出力先は、ディストリビューションのバージョンやBIOS/UEFI起動によって異なるため、使用している環境の設定を確認してください。[3]。

カーネルへオプションを渡す

GRUB 2の編集画面でlinuxから始まる行、または一部のUEFI向けGRUB実装で用いられるlinuxefiから始まる行の末尾に文字列を追記すると、その内容がカーネルの起動オプション(カーネルパラメータ)としてカーネルに渡されます。

カーネルパラメータの例効果
single(または1)SysVinit互換のシングルユーザモード指定。systemd環境でレスキューモードを指定する場合はsystemd.unit=rescue.targetを使用する
init=/bin/bash通常のinitに代えて/bin/bashを指定する(緊急時のトラブルシューティング用)。initramfsを使用する構成では、まずinitramfs上の/initが起動する場合がある
quiet起動時の詳細なメッセージ表示を抑える
root=/dev/sda1ルートファイルシステムとして使用するデバイスを明示的に指定する

恒久的に反映する場合は、ブートローダの節と同様に/etc/default/grubのGRUB_CMDLINE_LINUX(またはGRUB_CMDLINE_LINUX_DEFAULT)にパラメータを追記してから設定を再生成します。

Secure Boot

Secure Bootは、UEFIがOS起動時にブートローダーなどの電子署名を検証する仕組みです。 信頼された鍵で署名されたプログラムだけを実行し、不正なプログラムの起動を防ぎます。

Linuxを起動するには、Secure Bootに対応した署名済みブートローダーが必要です。 未対応のOSやブートローダーを使用する場合は、UEFIの設定でSecure Bootを無効化することがあります。

カーネルの読み込みとinitramfs

ブートローダは、カーネル本体に加えてinitramfs(initial RAM filesystem)という、圧縮されたファイルの集合もメモリへ読み込みます。initrdは本来はinitramfsとは異なる初期RAMディスクの仕組みを指しますが、initramfsのイメージファイルが慣例的にinitrdという名前で置かれる場合もあります。カーネルは、initramfsの内容を一時的なルートファイルシステムとしてメモリ上に展開してから、本来のルートファイルシステムへの切り替えを行います。

画像

initramfsが必要になる代表的な理由は、カーネルが本来のルートファイルシステムにアクセスするために必要なドライバ(ストレージコントローラのドライバ、LVM・RAID・暗号化ボリュームを扱うためのツール等)が、起動時点ではまだ用意されていない場合があるためです出典:openSUSE公式ドキュメント「Introduction to the Boot Process」。initramfsは、これらのドライバやツールを一時的な環境として提供し、本来のルートファイルシステムを認識・マウントできる状態を整えます。[4]。

ディストリビューション系統initramfsを生成・更新するコマンド
Debian・Ubuntu系update-initramfs -u(既存のinitramfsを更新)、update-initramfs -c -k バージョン(指定したカーネルバージョン向けに新規作成)
Red Hat・Fedora系dracut(dracut --forceで既存のものを上書き再生成)

必要なドライバの読み込みや、本来のルートファイルシステムを扱うための準備が整うと、initramfs上の/initが本来のルートファイルシステムへ切り替え、本来のルートファイルシステム上のinitを実行します。このとき、通常はPID 1の実行プログラムが置き換わる形で、起動処理が引き継がれます出典:openSUSE公式ドキュメント「Introduction to the Boot Process」。同ドキュメントでは、systemdを採用する環境では、initramfs上でも一度systemdが動き、本来のルートファイルシステムへの切り替え後に本来のルートファイルシステム上のsystemdが起動処理を引き継ぐ、という流れが説明されています。[5]。

init:システムが最初に起動するプロセス

initは、起動処理を担う、PID(プロセスID)が1のプロセスの呼び名です。initramfsを使用する場合、カーネルはまずinitramfs上の/initを起動し、本来のルートファイルシステムへの切り替え後は、その上のinitが起動処理を引き継ぎます。initは、ユーザー空間の他のすべてのプロセスの直接・間接の親となり、システムが動作を続ける限り存在し続けます。「init」という名前は特定の1つのプログラムだけを指すわけではなく、採用している仕組みに応じて実体が異なります。

SysVinitとsystemdの理解

項目SysVinitsystemd
起動の方式シェルスクリプト(/etc/rc.d/や/etc/init.d/配下等)を、ランレベルごとに決められた順序で1つずつ実行するサービス間の依存関係を定義した「ユニット」の単位で管理し、依存関係が無いもの同士は並行して起動できる
起動の速度順番に実行するため、サービス数が多いと起動に時間がかかりやすい依存関係の無い処理を並行して進められるため、一般的に起動が高速
個々のサービスの管理単位起動・停止用のシェルスクリプトユニットファイル(.serviceファイル等)

SysVinitは、Unix系OSで伝統的に使われてきた、シェルスクリプトを順番に実行する方式のinit実装ですSysV init方式のランレベル・/etc/inittab・telinitコマンドの詳細な使い方は「ブートプロセスとsystemd」の技術解説記事で扱っています。[6]。これに対しsystemdは、サービス間の依存関係をもとに可能な限り並行して起動を進めることや、必要になった時点で初めてサービスを起動する「ソケットアクティベーション」のような仕組みを取り入れた、比較的新しい実装です。現在の主要なディストリビューションの多くはsystemdを採用していますが、組み込み機器や一部のディストリビューションではSysVinitや、SysVinit互換の仕組みが使われ続けている場合もありますすべてのディストリビューションが必ずsystemdを採用しているとは限らない点に注意してください。[7]。

Upstartの知識

Upstartは、Canonical社の開発者によって作られた、SysVinitに代わるイベント駆動型のinit実装です。従来のSysVinitが、あらかじめ決められた順序でスクリプトを実行する同期的な仕組みだったのに対し、Upstartはハードウェアの挿抜(USBデバイスの接続等)のような「イベント」の発生に応じて非同期に処理を行える点を特徴としていました出典:Upstartの開発経緯(Wikipedia「Upstart (software)」)。[8]。

UpstartはUbuntu 6.10(2006年)でSysVinitに代わって採用され、Ubuntu 9.10(2009年)では起動処理全体がUpstart対応になりました。しかし2014年、Debianの技術委員会がsystemdを採用する決定を行ったことをきっかけに、Ubuntuも上流(Debian)との一貫性を保つ方針でsystemdへの移行を進め、Ubuntu 15.04(2015年)でデフォルトのinitがsystemdに切り替わりましたUpstartの最終リリースは2014年9月のバージョン1.13.2で、2018年12月以降は保守のみの状態が続いています。[9]。出題範囲ではUpstartについて「知識を示す(Awareness)」ことが求められているにとどまり、SysVinitやsystemdほど詳細な操作方法までは求められていません。

ブートイベントをログファイルで確認する

システムの起動中に何が起きたかを確認するための代表的なコマンドがdmesgとjournalctlです。両者は扱う対象・保存期間が異なります。

コマンド扱う対象保存期間・注意点
dmesgカーネルのリングバッファ(カーネル自身が出力したメッセージのみ)メモリ上の固定サイズのバッファのため、メッセージが増えると古いものから上書きされ、再起動すると内容は失われる
journalctlsystemd-journaldが収集し、systemdジャーナルに記録したメッセージ(カーネル・各サービス双方を含む)永続化が有効な環境では、複数回分の起動のログを再起動後も参照できる(永続化の設定方法は「システムロギング」の技術解説記事を参照)

dmesg

dmesg [オプション]
オプション意味
(オプション無し)リングバッファの内容をすべて表示する
-Tタイムスタンプを人間が読みやすい形式で表示する-Tのタイムスタンプは、システムがサスペンド・レジュームを経ている場合に不正確になることがあります(dmesg(1)マニュアルページに明記されています)。[10]
-w新しいメッセージが追加されるのを待ちながら表示し続ける(tail -fに近い動作)
-l レベル指定した優先度(err、warn等)のメッセージだけに絞り込む
-c内容を表示したうえで、リングバッファをクリアする

使用例:

dmesg -T | less

journalctlでブートごとのログを確認する

journalctlの一般的な検索・絞り込みオプション(-uによるユニット指定、-pによる優先度の絞り込み等)は「システムロギング」の技術解説記事で扱っています。ここでは、ブート(起動)ごとのログを確認するためのオプションに絞って補足します。

オプション意味
-b(引数無し)今回の起動分のログだけに絞り込む
-b -11つ前の起動分のログに絞り込む
--list-bootsこれまでに記録されている起動の一覧を、起動ごとの番号・開始時刻・終了時刻とともに表示する
-k(--dmesg)カーネルが出力したメッセージだけに絞り込む(_TRANSPORT=kernelに一致するもの。明示的に-bを指定しない限り、今回の起動分に絞り込まれる)

使用例:

journalctl --list-boots
journalctl -b -1 -p err

-b -1のように過去の起動分を参照できるのは、永続化されたジャーナルが残っている場合に限られます。前述のとおり、ジャーナルの永続化が有効になっていない環境では、過去の起動分のログは参照できません。


補足

  1. UEFIはBIOSより新しい規格で、多くの現行PC・サーバーで採用されています。UEFI環境でも、互換性のためにBIOS相当の起動方式(レガシーBIOSモード)に切り替えられる機種があります。 ↩
  2. 出題範囲の用語一覧には「bootloader」という一般的な語のみが挙げられており、特定の製品名は明記されていません。本記事では、実務・学習の両面で広く使われているGRUB 2を具体例として説明します。 ↩
  3. update-grubは多くのDebian系ディストリビューションでgrub-mkconfig -o /boot/grub/grub.cfgを呼び出す簡易コマンドとして提供されています。Red Hat系でのgrub2-mkconfigの出力先は、ディストリビューションのバージョンやBIOS/UEFI起動によって異なるため、使用している環境の設定を確認してください。 ↩
  4. 出典:openSUSE公式ドキュメント「Introduction to the Boot Process」。initramfsは、これらのドライバやツールを一時的な環境として提供し、本来のルートファイルシステムを認識・マウントできる状態を整えます。 ↩
  5. 出典:openSUSE公式ドキュメント「Introduction to the Boot Process」。同ドキュメントでは、systemdを採用する環境では、initramfs上でも一度systemdが動き、本来のルートファイルシステムへの切り替え後に本来のルートファイルシステム上のsystemdが起動処理を引き継ぐ、という流れが説明されています。 ↩
  6. SysV init方式のランレベル・/etc/inittab・telinitコマンドの詳細な使い方は「ブートプロセスとsystemd」の技術解説記事で扱っています。 ↩
  7. すべてのディストリビューションが必ずsystemdを採用しているとは限らない点に注意してください。 ↩
  8. 出典:Upstartの開発経緯(Wikipedia「Upstart (software)」)。 ↩
  9. Upstartの最終リリースは2014年9月のバージョン1.13.2で、2018年12月以降は保守のみの状態が続いています。 ↩
  10. -Tのタイムスタンプは、システムがサスペンド・レジュームを経ている場合に不正確になることがあります(dmesg(1)マニュアルページに明記されています)。 ↩