Linux kernelをビルドする
連載ナビ
この連載は「Linuxを使えるようになる」ではなく、「Linuxがどう動いているのか」を理解するための実践シリーズです。
今回は Linuxを理解する 〜 LFSで学ぶLinux内部構造 〜 の Part 5 / 第1回として、仕組みのつながりを意識しながら学びます。
- 前回: chrootを理解する
- 次回: initramfsとは何か
—
カーネルビルドとは「設定 → コンパイル → 配置」の3工程
Linuxカーネルのビルドと聞くと特殊な作業に思えますが、結論から言うと、やることは次の3工程だけです。
1. 設定: make menuconfig などで「どの機能を組み込むか」を選び、.config ファイルを作る
2. コンパイル: make でソースコードから vmlinuz(カーネル本体)とカーネルモジュール群を生成する
3. 配置: できあがったカーネルを /boot に置き、モジュールを /lib/modules/ にインストールし、ブートローダ(GRUB)に登録する
普段Ubuntuで見かける /boot/vmlinuz-6.8.0-45-generic のようなファイルは、Ubuntuの開発チームがこの3工程を実行した成果物です。LFSでは、これを自分の手で行います。カーネルビルドは「OSの核を自分で決めて作る」作業であり、この連載の大きな山場です。
ディストリビューションのカーネルは「全部入り」だから
なぜ自分でビルドする価値があるのか。理由は、普段使っているカーネルが「どんなハードウェアでも動く全部入り構成」であり、その構成の中身を選ぶ機会が一度もないからです。
Ubuntuのカーネルは、無数のマシンで動く必要があるため、数千個のデバイスドライバやファイルシステムをモジュールとして同梱しています。一方、カーネルの各機能は設定時に次の3択から選べます。
y— カーネル本体に組み込む(起動直後から使える。その分カーネルが大きくなる)m— モジュールとして別ファイルにする(必要になったときにロードされる)n— 無効にする(そもそもコンパイルしない)
この選択が起動の成否に直結します。たとえば、ルートファイルシステムに使っているext4や、ディスクコントローラのドライバを m(モジュール)にすると、「モジュールはディスクの中にあるのに、そのディスクを読むためにモジュールが必要」という循環に陥り、カーネルは起動に失敗します。自分で選んでみて初めて、「Ubuntuはこの問題をどう回避しているのか」(答えは次回のinitramfsです)という問いが実感を持って理解できるようになります。
ビルドの全工程を追う
実際の流れを、コマンドと生成物で追ってみます。ソースは kernel.org から入手し、展開したディレクトリで作業します。
# 1. 設定画面を開く(.config が生成される)
make menuconfig
# 2. CPUコア数を使って並列コンパイル(マシン性能により数十分〜数時間)
make -j$(nproc)
# 3. モジュールを /lib/modules/<バージョン>/ にインストール
sudo make modules_install
# 4. カーネル本体を /boot に配置
# x86_64 の場合(M1 Mac上のARM64 VMなら arch/arm64/boot/Image)
sudo cp arch/x86_64/boot/bzImage /boot/vmlinuz-lfs
sudo cp System.map /boot/System.map-lfs
sudo cp .config /boot/config-lfs
make menuconfig は、ncursesライブラリを使ったテキストベースの設定画面です(未導入なら ncurses の開発パッケージが必要です)。設定結果はソースディレクトリ直下の .config という1つのテキストファイルに、CONFIG_EXT4_FS=y のような行として保存されます。ゼロから選ぶのは大変なので、実務では動作中のシステムの設定(多くのディストリビューションでは /boot/config-$(uname -r) に置かれています)をコピーして出発点にするのが定石です。
make が完了すると、x86_64なら arch/x86_64/boot/bzImage、ARM64なら arch/arm64/boot/Image という圧縮済みカーネルイメージができます。これを /boot にコピーしてGRUBのメニューに登録すれば、次回起動時に自分のカーネルが選べるようになります。
設定を1つ間違えるとどうなるか
機能選択の重みを体感できる典型的な失敗が、ルートファイルシステムのドライバを組み込み忘れた場合です。起動すると、カーネルは途中まで正常に進んだあと、こんなメッセージで停止します。
VFS: Cannot open root device "sda2" or unknown-block(8,2): error -6
Please append a correct "root=" boot option; here are the available partitions:
Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(8,2)
これは「カーネル自体は起動したが、ルートファイルシステムをマウントできなかった」という状態です。原因の多くは次のどれかです。
- ルートに使っているファイルシステム(ext4など)を
yにしていない - ディスクコントローラのドライバ(仮想環境なら
virtio_blkなど)をyにしていない - GRUBに渡す
root=パラメータの指定ミス
逆に言えば、このpanicメッセージを読んで「ドライバか、ファイルシステムか、root= 指定か」と切り分けられるようになることが、カーネルビルドを経験する最大の収穫です。普段のUbuntuでこのエラーに出会うことはまずありませんが、クラウドでカスタムイメージを作るときや、コンテナホストのカーネルを調整するときに、この理解がそのまま生きてきます。
カーネルは「与えられるもの」から「選んで作るもの」へ
カーネルビルドは、設定(.config)・コンパイル(make)・配置(/boot とGRUB)の3工程であり、機能ごとの y/m/n の選択が起動の成否に直結する——これが今回のポイントです。
そして「ext4ドライバをモジュールにしたら起動できない」という循環問題には、実はディストリビューションが使っている標準的な解決策があります。それが次回のテーマ、initramfsです。Ubuntuの /boot に必ず initrd.img が置いてある理由が、次回はっきり分かります。
図解テキスト
kernel source
-> make menuconfig (.config を作る。各機能を y/m/n で選択)
-> make (コンパイル)
-> bzImage/Image (カーネル本体 → /boot へ)
+ modules (→ /lib/modules/<version>/ へ)
-> GRUB に登録して再起動
この回で実行するコマンド
上から順番に実行すれば、この回の内容を体験できます。
まずはそのままコピペして実行し、次に1行ずつ意味を確認してください。
(menuconfig 以降はカーネルソースを展開したディレクトリで実行します)
# いま動いているカーネルのバージョンと設定ファイルを確認
uname -r
ls /boot/
# カーネルソースのディレクトリで: 設定画面を開く
make menuconfig
# 並列ビルド(nproc が使えない環境向けのフォールバック付き)
make -j$(nproc 2>/dev/null || sysctl -n hw.ncpu)
ハンズオン手順
1. ターミナルを開いて、上のコマンドを1行ずつ実行する
2. make menuconfig で「File systems」の項目を開き、ext4 が y/m のどちらかをメモする
3. 生成された .config を grep EXT4 .config で確認し、画面での選択と一致するか見る
4. エラーが出たら、そのエラーメッセージをそのまま残す
チェックポイント
- コマンドが最後まで実行できた
y/m/nの違いを、起動への影響とあわせて説明できる- 「Unable to mount root fs」の原因の候補を2つ挙げられる
- 次の回に進む前に、分からない単語を1つ調べた
今回理解できたこと
- kernelは自分で構成できる(設定は
.configに集約される) - 機能選択が起動結果に直結する(ルートFSのドライバは特に重要)
- ビルドの成果物は
/bootのカーネル本体と/lib/modules/のモジュール群