之前 Linux の起動プロセスの概要を見ました:
もっと具体的な詳細を知りたかったので、いくつかの情報を検索してまとめました。
一、CPU リセット#
電源ボタンを押すと、デバイスに電力が供給され始めますが、最初の電力供給は安定していません。メインボードはこれを検出すると、CPU にリセット信号を継続的に送信し、この時点で CPU はレジスタの残留データをクリアし、レジスタの設定を行います:
ここで最も重要なレジスタはCS と EIPです。CS の暗黙のベースは RIP と加算され、つまりとなり、このアドレス(リセットベクトルとも呼ばれる)にはジャンプ命令が格納されています。ジャンプ先のアドレスはBIOS のエントリポイントアドレスです。
なぜリセットベクトルを直接 BIOS のエントリポイントアドレスに設定せずにジャンプするのですか?
X86 チップは最初にリアルモードで実行されますが、リアルモードでは 20 ビットのアドレッシングスペース(1M)しかありません。リセットベクトルは fff0H であり、ffffH までの距離は非常に短いため、BIOS プログラムを格納するのに十分なスペースがありません。そのため、ジャンプする必要があります。
リセットベクトルをアドレススペースの高いアドレスに配置する理由は、より大きなメモリスペースを確保するためです。
リアルモード下のメモリレイアウト
マルチコアシステムでの CPU リセット#
マルチコアコンピュータでは、最初にプロトコルを実行するために、システムは 1 つの CPU を選択し、他の CPU は待機状態になります。選択されたメイン CPU はブートストラッピング CPU(BSP)と呼ばれ、BSP だけが続行し、他の CPU は BSP からの命令を待ちます。
二、BIOS の実行#
BIOS は EPROM に固定されたプログラムであり、通常はハードウェアメーカーによって固定されており、システムの起動を担当しています。
POST 電源自己テスト#
BIOS が最初に実行するプログラムは POST です。これはコンピュータが電源を入れた直後にハードウェアの一部をテストするために使用されます。自己テスト中に重大なエラーが見つからない場合、システムはテストコードに基づいてメッセージを表示するかブザーで警告を発することがあります。
なぜブザーを使って警告を発するのですか?
以前はオンボードグラフィックスがなかったため、グラフィックカードは必ず外部デバイスでした。POST が実行されるとき、グラフィックカードはまだ初期化が完了していないため、エラーメッセージを画面に表示することはできず、音で警告するしかありませんでした。
テストは 1 つずつ実行され、BIOS メーカーは各デバイスに対して 1 つのPOST CODE
を提供しています。デバイスをテストするときに、この POST CODE が診断ポートにロードされ、テストに合格しない場合はこの CODE が保持され、警告が発生します。
デバイスの初期化#
POST が終了すると、BIOS は各外部デバイスの BIOS を呼び出して自己テストと初期化を行います。たとえば、グラフィックカードの BIOS など、すべてのデバイスはこの時点で初期化されて起動します。
デバイスの初期化以外にも、BIOS はこの時点で割り込みベクタテーブルを初期化します。
ブートローダの起動#
各種デバイスの検出と初期化が完了した後、BIOS はユーザー定義のブート順序を検索します。デフォルトではディスクですが、光学ディスクや USB メモリ(以前はオペレーティングシステムを再インストールするときに使用されました)などに変更することもできます:
順序に従って、BIOS は最初に来るデバイスのMBR(メインブートレコード)、つまり 0 トラックの最初のセクタの 512 バイトをRAM の絶対アドレス 0x7C00に読み込み、そのアドレスにジャンプします。
MBR フォーマット
MBR はどのオペレーティングシステムにも属していないため、その 512 バイトの内容は次のようになります:
- ブートコード(446 バイト):パーティションテーブルの正確性をチェックし、ハードディスク上のブートローダプログラム(例:grub)にシステム制御を転送します。
- ディスクパーティションテーブル(16 X 4 バイト):DPT は、4 つのパーティションテーブルで構成され、各パーティションテーブルは 16 バイトで、ディスクのパーティション状況を説明します。
- 終了マーカー(2 バイト)
UEFI
現在の BIOS については、通常 UEFI を指します。従来の BIOS でも UEFI でも、ROM→RAM→BOOT のプロセスを経ていますが、主要な部分には違いがありません。では、なぜ UEFI が必要なのでしょうか?
この回答を参照してください:UEFI 引导与 BIOS 引导在原理上有什么区别?
三、ブートローダ#
ブートローダプログラムは、オペレーティングシステムのカーネルファイルをメモリにロードするためのものであり、このタイプのプログラムの主な機能は次のとおりです:
- リアルモードからプロテクトモードへの移行、16 ビットアドレッシングスペースから 32 ビットアドレッシングスペースへの移行、セグメントメカニズムの有効化。
- ハードディスクから ELF 形式のカーネル(MBR の後に続くセクタ)を読み込んで固定のメモリ位置に配置します。このプロセスは通常 2 つのステップで行われ、最終的には boot 命令が実行され、システムのブートメニュー(
/boot/grub/menu.lst
または grub.lst)、カーネル vmlinuz、および RAM ディスク initrd がロードされます。
ここでは GNU のgrubを例に挙げます。grub は、異なるカーネルを持つオペレーティングシステムのパーティションを選択するために使用できます。また、これらのカーネルに起動パラメータを渡すこともできます。grub のロードは通常 2 つのステップで行われます: - 基本ブートプログラム - stage1 をロードします。その主な機能は、第 2 ブートプログラムである stage2 をロードすることです。
- 第 2 ブートプログラム - stage2 をロードします。この第 2 ブートローダプログラムは、より高度な機能を提供するために使用され、ユーザーに特定のオペレーティングシステムをロードすることを許可します。grub では、このステップではユーザーに表示メニューを表示するか、ユーザーにコマンドを入力することができます。このステージのブートの最終的な状態は、boot コマンドを実行してオペレーティングシステムカーネルをメモリにロードし、制御をカーネルに渡すことです。
カーネルのロードが完了すると、メモリは次のようにマップされます:
四、Linux カーネルの設定と起動#
Linux カーネルが起動すると、一連のチェックが実行され、チェックが完了すると start_kernel 関数にジャンプし、この関数は各モジュールを順番に初期化します。たとえば、ページテーブル、割り込みベクタなどです。その後、0 番プロセスになります。
0 番プロセスは 1 番の kernel_init プロセスを fork し、kernel_init は init プログラムを実行します。大まかなプロセスは次のようになります:
init プログラム
- init プロセスは
/etc/inittab
ファイルを読み取り、Linux のランレベルを設定し、プロセスの実行モードを決定します。- Linux は最初のユーザーレベルファイル
/etc/rc.d/rc.sysinit
を実行します。このファイルの機能には、PATH 実行変数の設定、ネットワーク設定、スワップパーティションの起動、/proc の設定、システム関数などが含まれます。/etc/modules.conf
および/etc/modules.d
ディレクトリのファイルを読み取り、システムカーネルモジュールをロードします。- いくつかのサービスを起動し、その後
/etc/rc.d/rc.local
ファイルを実行します。- 最後に /bin/login プログラムを実行し、ログイン画面に移行し、ユーザーにユーザー名とパスワードを入力させます。
ユーザーの起動が完了すると、0 番プロセスは cpu_idle に入り、idle プロセスになります。これで Linux の起動はほぼ完了です。