【Linux】カーネル について
はじめに
Ubuntu 環境でカーネルをビルドする機会があったため、Linux カーネルの基本について整理する。
カーネルについて
・OS の中枢プログラム
・システム起動後にメモリに常駐する
・コンピューターのハードウェア管理やプロセス管理などを行う
・ユーザーアプリケーションはカーネルを通してハードウェアを操作する
カーネルの役割の例
例えば USB メモリを挿入したとき、「USB が挿入されたことを検出する」「適切なデバイスドライバを動かす」「デバイスとして利用可能にする」といった処理を行っているのがカーネルである。
また、キーボードで文字を打ったときにも、カーネルが割り込みとして入力を受信し、その情報がアプリケーションに渡されることで、最終的に画面に文字が表示される。
さらに、ls コマンドを実行すると、シェルの要求に応じてカーネルがプロセスを生成し、CPU やメモリなどの資源を割り当ててプログラムを実行している。
ユーザー空間とカーネル空間
Linux では、アプリケーションが動作するユーザー空間と、OS の中枢であるカーネルが動作するカーネル空間が明確に分離されている。この分離により、システムの安定性と安全性が保たれている。
アプリケーションはハードウェアを直接操作することはできず、ファイルアクセスやデバイス操作などは、すべてカーネルを介して行われる。カーネルは、アプリケーションとハードウェアの間に立ち、資源の管理と安全な制御を担う存在である。ユーザー空間のアプリケーションは、システムコールを通じてカーネルに処理を依頼し、間接的にハードウェア操作を行う。
- ユーザー空間:一般のアプリケーションが動く場所(ハードウェアへ直接アクセスできない)
- カーネル空間:カーネル本体やデバイスドライバが動作する場所
カーネルイメージについて
カーネルイメージについては、以下のとおりである
・起動時にメモリへ読み込まれて実行される「Linux カーネル本体の実行ファイル」
・ブートローダがロードする特別なバイナリ
・多くの Linux ディストリビューションでは /boot/ 以下に カーネルイメージが配置される
モジュールについて
Linux カーネルでは、機能の一部を「モジュール」として分離して管理することができる。
モジュール化された機能は、必要に応じて動的にロード・アンロードが可能であり、すべてをカーネル本体に組み込む必要はない。
今回のビルド作業では、make modules によってカーネルモジュールがビルドされ、
make modules_install により /lib/modules/ 以下へ配置される。
udev について
udev は、カーネルが検出したデバイス情報をもとに、ユーザー空間でデバイス管理を行う仕組みである。
カーネルは USB やディスクなどのハードウェアを検出し、その結果をユーザー空間に通知するが、実際に /dev/ 以下にデバイスファイルを作成したり、名前付けや権限設定を行っているのは udev である。
以下コマンドでわかる通り、ユーザー空間で常駐しているデーモンであり、必要に応じて modprobe を通じて、カーネルにデバイスドライバ(モジュール)のロードを要求する。
ubuntu@ubuntu-srv:/usr/src/linux-6.17.13$ systemctl list-units | grep -i udev
systemd-udev-trigger.service loaded active exited Coldplug All udev Devices
systemd-udevd.service loaded active running Rule-based Manager for Device Events and Files
systemd-udevd-control.socket loaded active running udev Control Socket
systemd-udevd-kernel.socket loaded active running udev Kernel Socket
/proc/sys と sysctl について
/proc/sys とは
/proc/sysは、カーネルの内部パラメータをファイルとして参照・変更できる仮想ファイルシステムである。
/proc/ 以下に存在するファイルであることから分かる通り、これらはディスク上の実体ではなく仮想的に提供されている。
sysctl とは
sysctlは、/proc/sys以下の値をコマンドや設定ファイルを通じて管理するための仕組みである。これにより、カーネルの動作パラメータを再起動せずに確認・変更することができる。
また、/etc/sysctl.conf や /etc/sysctl.d/*.conf に設定を記述することで、起動時にカーネルパラメータを自動的に反映させることができる。
カーネルのコンパイル
カーネルを自分でコンパイルするのは、以下のようなケースである。
・必要なデバイスドライバがカーネルに含まれていない
・使用するハードウェアに最適化したカーネルを利用したい
・カーネルを自分でビルドして最新機能を試したい
カーネルコンパイル実施
さいごに、理解を深めため、カーネルコンパイルを実践してみる。
以下は実践環境
- OS : 24.04.3 LTS (vmware 仮想マシン)
- 元のカーネルバージョン : 6.8.0-90-generic
- メモリ : 4GB
- ディスク : 70GB(nvme0n1)
おおまかな流れ
- カーネルソースを用意
- カーネルコンフィグレーションを設定
- カーネルをコンパイル
- モジュールをコンパイル
- カーネルとモジュールを配置
- ブートローダの設定を変更
カーネルソースの取得
まず、https://kernel.org/ からカーネルソースをダウンロードする。

今回は安定性が高く、ドキュメントや事例も多い stable 版の6.18.3を取得し、これをコンパイルしてみる。
カーネルソースは慣例的に /usr/src 以下に配置されることが多いため、今回はこのディレクトリを使用する。そして、/usr/src は root 権限が必要なため、sudo を使用してダウンロードする。
ubuntu@ubuntu-srv:~$ cd /usr/src/
ubuntu@ubuntu-srv:/usr/src$ sudo wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.18.3.tar.xz
ubuntu@ubuntu-srv:/usr/src$ ls -l linux-6.18.3.tar.xz
-rw-r--r-- 1 root root 154312116 Jan 2 12:05 linux-6.18.3.tar.xz
カーネルソースの解凍
ダウンロードしたカーネルソースは .tar.xz 形式で圧縮されているため、まずは解凍する。
ubuntu@ubuntu-srv:/usr/src$ sudo tar xf linux-6.18.3.tar.xz
解凍後、linux-6.18.3 ディレクトリが作成される。
ubuntu@ubuntu-srv:/usr/src$ ls -ld linux-6.18.3*
drwxrwxr-x 26 root root 4096 Jan 2 11:57 linux-6.18.3
-rw-r--r-- 1 root root 154312116 Jan 2 12:05 linux-6.18.3.tar.xz
以降の作業は、このディレクトリ内で行う。
ubuntu@ubuntu-srv:/usr/src$ cd linux-6.18.3/
カーネル設定(.config)の作成
カーネルをビルドするには、どの機能やドライバを有効にするかを定義した設定ファイル(.config)が必要となる。ここでは、稼働中のカーネル設定をベースに .config を作成する。
ubuntu@ubuntu-srv:/usr/src/linux-6.18.3$ sudo cp /boot/config-6.8.0-90-generic .config
この状態では、設定ファイルは存在するが、新しいカーネルバージョンに追加された項目が未反映であるため、設定を更新する。
ubuntu@ubuntu-srv:/usr/src/linux-6.18.3$ sudo make oldconfig
make oldconfig を実行すると、新しく追加された設定項目について対話的に選択を求められる。
かなりの数の確認事項があるが、すべて Enter を押しデフォルト値を採用する。
make oldconfig は現在のカーネル設定を新しいカーネルの設定(.config)に反映させるコマンド。
一から設定ファイルを作成する場合は、make menuconfig コマンドを実行する。
証明書機能の無効化
ubuntu@ubuntu-srv:/usr/src/linux-6.18.3$ sudo scripts/config --disable SYSTEM_TRUSTED_KEYS
ubuntu@ubuntu-srv:/usr/src/linux-6.18.3$ sudo scripts/config --disable SYSTEM_REVOCATION_KEYS
設定変更後は、依存関係を反映するため再度 make oldconfig を実行する。
ubuntu@ubuntu-srv:/usr/src/linux-6.18.3$ sudo make oldconfig
#
# No change to .config
#
ビルド前の注意点(ディスク容量)
カーネルのビルドでは、多数のオブジェクトファイルやモジュールが生成されるため、十分なディスク容量が必要となる。
実際に40GBのストレージで実践した場合、make modules の途中で以下のようなエラーが発生した。
ld: final link failed: No space left on device
このエラーは、カーネルモジュールのリンク中にディスク容量が不足したことを示している。
そのため、事前に df -h や lsblk を用いて、十分な空き容量があることを確認しておく必要がある。(今回はルートパーティションを拡張し、約70GB のディスク容量を確保した)
カーネルのビルド
make oldconfig 実行後、カーネルのビルドを行う。
この処理では、Linux カーネル本体(vmlinux)や組み込みドライバがビルドされる。
-j$(nproc) を指定することで、CPU コア数分の並列ビルドが行われ、ビルド時間を短縮できる。
ubuntu@ubuntu-srv:/usr/src/linux-6.18.3$ sudo make -j$(nproc)
続いて、カーネルモジュールをビルドする。
ubuntu@ubuntu-srv:/usr/src/linux-6.18.3$ make modules
カーネルモジュールのインストール
ビルドが完了しただけでは、作成されたカーネルモジュール(.ko ファイル)はまだシステムから利用できない状態である。そのため、次にカーネルモジュールのインストールを行う。
ubuntu@ubuntu-srv:/usr/src/linux-6.18.3$ sudo make modules_install
SYMLINK /lib/modules/6.18.3/build
INSTALL /lib/modules/6.18.3/modules.order
...省略...
SIGN /lib/modules/6.18.3/kernel/net/qrtr/qrtr-mhi.ko
DEPMOD /lib/modules/6.18.3
このコマンドで、ビルドされたカーネルモジュールが以下のディレクトリに配置される。
あわせて depmod が実行され、モジュール間の依存関係情報が生成される。
これにより、modprobe などを用いたモジュールの自動ロードが可能になる。
ubuntu@ubuntu-srv:/usr/src/linux-6.18.3$ ls -lF /lib/modules/6.18.3/
total 8520
lrwxrwxrwx 1 root root 21 Jan 8 09:18 build -> /usr/src/linux-6.18.3/
drwxr-xr-x 13 root root 4096 Jan 8 09:18 kernel/
-rw-r--r-- 1 root root 2130362 Jan 8 09:21 modules.alias
-rw-r--r-- 1 root root 2060443 Jan 8 09:21 modules.alias.bin
-rw-r--r-- 1 root root 24501 Jan 8 09:18 modules.builtin
-rw-r--r-- 1 root root 138116 Jan 8 09:21 modules.builtin.alias.bin
-rw-r--r-- 1 root root 26339 Jan 8 09:21 modules.builtin.bin
-rw-r--r-- 1 root root 281440 Jan 8 09:18 modules.builtin.modinfo
-rw-r--r-- 1 root root 878631 Jan 8 09:21 modules.dep
-rw-r--r-- 1 root root 1199569 Jan 8 09:21 modules.dep.bin
-rw-r--r-- 1 root root 317 Jan 8 09:21 modules.devname
-rw-r--r-- 1 root root 315121 Jan 8 09:18 modules.order
-rw-r--r-- 1 root root 1678 Jan 8 09:21 modules.softdep
-rw-r--r-- 1 root root 737354 Jan 8 09:21 modules.symbols
-rw-r--r-- 1 root root 897497 Jan 8 09:21 modules.symbols.bin
カーネル本体のインストール
続いて、ビルドしたカーネル本体をインストールする。
ubuntu@ubuntu-srv:/usr/src/linux-6.18.3$ sudo make install
INSTALL /boot
run-parts: executing /etc/kernel/postinst.d/initramfs-tools 6.18.3 /boot/vmlinuz-6.18.3
update-initramfs: Generating /boot/initrd.img-6.18.3
run-parts: executing /etc/kernel/postinst.d/unattended-upgrades 6.18.3 /boot/vmlinuz-6.18.3
run-parts: executing /etc/kernel/postinst.d/update-notifier 6.18.3 /boot/vmlinuz-6.18.3
run-parts: executing /etc/kernel/postinst.d/xx-update-initrd-links 6.18.3 /boot/vmlinuz-6.18.3
I: /boot/initrd.img is now a symlink to initrd.img-6.18.3
run-parts: executing /etc/kernel/postinst.d/zz-shim 6.18.3 /boot/vmlinuz-6.18.3
run-parts: executing /etc/kernel/postinst.d/zz-update-grub 6.18.3 /boot/vmlinuz-6.18.3
Sourcing file `/etc/default/grub'
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-6.18.3
Found initrd image: /boot/initrd.img-6.18.3
Found linux image: /boot/vmlinuz-6.8.0-90-generic
Found initrd image: /boot/initrd.img-6.8.0-90-generic
Warning: os-prober will not be executed to detect other bootable partitions.
Systems on them will not be added to the GRUB boot configuration.
Check GRUB_DISABLE_OS_PROBER documentation entry.
Adding boot menu entry for UEFI Firmware Settings ...
done
このコマンドにより、以下のファイルが /boot ディレクトリに配置される。
ubuntu@ubuntu-srv:/usr/src/linux-6.18.3$ ls /boot/*6.18.3*
/boot/config-6.18.3 /boot/initrd.img-6.18.3 /boot/System.map-6.18.3 /boot/vmlinuz-6.18.3
GRUB 設定の更新
念のため、明示的に GRUB の設定を更新しておく。
ubuntu@ubuntu-srv:/usr/src/linux-6.18.3$ sudo update-grub
Sourcing file `/etc/default/grub'
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-6.18.3
Found initrd image: /boot/initrd.img-6.18.3
Found linux image: /boot/vmlinuz-6.8.0-90-generic
Found initrd image: /boot/initrd.img-6.8.0-90-generic
Warning: os-prober will not be executed to detect other bootable partitions.
Systems on them will not be added to the GRUB boot configuration.
Check GRUB_DISABLE_OS_PROBER documentation entry.
Adding boot menu entry for UEFI Firmware Settings ...
done
ここまでで、カーネルのコンパイルが完了。
再起動して確認
マシンを再起動する。
ubuntu@ubuntu-srv:/usr/src/linux-6.18.3$ sudo reboot
再起動後のシステムのカーネルバージョンを確認すると、6.18.3に変更されていることを確認できる。
ubuntu@ubuntu-srv:~$ uname -r
6.18.3
まとめ
make
→ カーネル本体と組み込みドライバをビルド
make modules
→ カーネルモジュール(.ko)をビルド
make modules_install
→ モジュールを /lib/modules/<version>/ に配置
make install
→ カーネル本体を /boot に配置し、起動可能な状態にする
今回カーネルを実際にビルドしてみることで、普段は意識しない「起動時に何が起きているのか」「モジュールがどのように管理されているのか」を具体的なファイルやコマンド単位で理解することができた。
補足
カーネルバージョンの変更
起動時に使用するカーネルを変更するには、grub 設定を変更する。今回は元のカーネルバージョンに戻す設定を行う。
まず、/boot/grub/grub.cfgファイルからカーネルの起動エントリー情報を確認する。
menuentry 行が GRUB の起動候補であり、表示順がそのままメニュー番号に対応する。
ubuntu@ubuntu-srv:~$ sudo grep menuentry /boot/grub/grub.cfg
[sudo] password for ubuntu:
if [ x"${feature_menuentry_id}" = xy ]; then
menuentry_id_option="--id"
menuentry_id_option=""
export menuentry_id_option
menuentry 'Ubuntu' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-simple-68adb290-b286-4efe-ba8b-a952674f38ca' {
submenu 'Advanced options for Ubuntu' $menuentry_id_option 'gnulinux-advanced-68adb290-b286-4efe-ba8b-a952674f38ca' {
menuentry 'Ubuntu, with Linux 6.18.3' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-6.18.3-advanced-68adb290-b286-4efe-ba8b-a952674f38ca' {
menuentry 'Ubuntu, with Linux 6.18.3 (recovery mode)' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-6.18.3-recovery-68adb290-b286-4efe-ba8b-a952674f38ca' {
menuentry 'Ubuntu, with Linux 6.8.0-90-generic' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-6.8.0-90-generic-advanced-68adb290-b286-4efe-ba8b-a952674f38ca' {
menuentry 'Ubuntu, with Linux 6.8.0-90-generic (recovery mode)' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-6.8.0-90-generic-recovery-68adb290-b286-4efe-ba8b-a952674f38ca' {
menuentry 'UEFI Firmware Settings' $menuentry_id_option 'uefi-firmware' {
この結果から'Ubuntu, with Linux 6.8.0-90-generic'のエントリ番号は"1>2"であることがわかる。
GRUB のメニュー番号は 0 から始まる。
今回の構成では以下の対応関係になる。
- 0: Ubuntu
- 1: Advanced options for Ubuntu
- 0: Ubuntu, with Linux 6.18.3
- 1: Ubuntu, with Linux 6.18.3 (recovery mode)
- 2: Ubuntu, with Linux 6.8.0-90-generic
- 3: Ubuntu, with Linux 6.8.0-90-generic (recovery mode)
次に grub の設定ファイルである/etc/default/grubを修正する。
ubuntu@ubuntu-srv:~$ sudo cp -a /etc/default/grub /etc/default/grub.default
ubuntu@ubuntu-srv:~$ sudo vi /etc/default/grub
ubuntu@ubuntu-srv:~$ diff /etc/default/grub /etc/default/grub.default
6c6
< GRUB_DEFAULT="1>2"
---
> GRUB_DEFAULT=0
修正後に、grub 設定を反映する。
ubuntu@ubuntu-srv:~$ sudo update-grub
Sourcing file `/etc/default/grub'
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-6.18.3
Found initrd image: /boot/initrd.img-6.18.3
Found linux image: /boot/vmlinuz-6.8.0-90-generic
Found initrd image: /boot/initrd.img-6.8.0-90-generic
Warning: os-prober will not be executed to detect other bootable partitions.
Systems on them will not be added to the GRUB boot configuration.
Check GRUB_DISABLE_OS_PROBER documentation entry.
Adding boot menu entry for UEFI Firmware Settings ...
done
再起動して、カーネルが変更されていることを確認できたら完了。
ubuntu@ubuntu-srv:~$ uname -r
6.8.0-90-generic
Discussion