目次
カーネルのコンパイル
カーネルは、ソースコードからコンパイルすることで再構築ができる。必要なデバイスドライバがカーネルに含まれていないときや、ハードウェアに最適化したいとき、最新の機能を搭載したカーネルを利用したいときにはカーネルの再構築を行う。
コンパイルの手順
- コンフィギュレーションの設定
- カーネルソースコードをコンパイル
- カーネル及びカーネルモジュールを所定のパスに配置
- ブートローダの設定変更
カーネルソース
一般的に、カーネルのソースは”/usr/stc”以下に”linux-バージョン”のような名前で命名されたディレクトリに格納されている。また、最新のカーネルのソースは、The Linux Kernel Archivesから取得できる。The Linux Kernel Archivesで取得した変更が加えられていないカーネルをバニラカーネル(Vanilla Kernel)という。
ソースディレクトリには主に下記のようなディレクトリ構成になっている。ただし、ディレクトリ構成はバージョンやディストリビューションなどによって変わる。
ディレクトリ | 内容 |
arch | x86、ARM、MIPSなどの各種アーキテクチャに依存したコード |
block | ブロック型デバイス制御のための共通処理に関するコード |
certs | カーネルの署名に関連するコード |
crypto | 暗号化やハッシュ関数など、暗号処理に関連するコード |
Documentation | カーネルの各種ドキュメント |
drivers | 各種デバイスドライバ関連のファイル |
fs | ext4、btrfs、NFSなどの各種ファイルシステム関連のファイル |
include | C言語のヘッダーファイル。カーネル内部の共通定義やデータ構造が定義されている。 |
init | 初期化コード。カーネル起動時に最初に実行される。 |
io_uring | 効率的な非同期I/O操作を実現するためのカーネル機能であるio_uringのコード |
ipc | SystemV互換プロセス間通信関連のファイル |
Kbuild | ビルドシステムに関する設定ファイル。カーネルのコンパイル方法が定義されている。 |
Kconfig | カーネルの構成オプションを設定するファイル。コンフィギュレーションメニューの定義などが含まれる。 |
kernel | カーネルの機能 |
lib | 各種モジュール関連のファイル |
Makefile | カーネルのMakefile |
mm | メモリ管理関連のファイル |
Module.symvers | カーネルモジュール間のシンボル(関数や変数など)の依存関係を示すファイル。モジュールが互いに使用するシンボルを解析するために使用される。 |
net | 各種ネットワークプロトコル関連のファイル |
rust | カーネルの一部をRustで記述するための機能。 |
samples | カーネルの各種サンプルコード |
scripts | カーネルの作成支援スクリプト |
security | セキュリティ関連のコード |
sound | サウンドドライバや音声処理関連 |
tools | カーネルの開発や診断を支援するツール |
usr | ユーザー空間に関連するコード |
virt | 仮想化に関連するコード。KVM(Kernel-based Virtual Machine)や他の仮想化テクノロジーをサポートする。 |
カーネルソースのダウンロード
バニラカーネル(tarボール)を取得し、tarコマンドで展開してみる。The Linux Kernel Archivesからcurlコマンドでtarボール(今回はバージョンは6.10.6)をダウンロードする。ビルド作業は、一般ユーザーのホームディレクトリで問題ない。
$ curl -OL https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.10.6.tar.xz
tarコマンドで展開する。パーミッションで拒否されることがあるので”root”ユーザーに切り替えておく。
$ sudo su
$ xz -dc linux-6.10.6.tar.xz | tar xvf -
展開したディレクトリを確認する。
$ ls
linux-6.10.6 linux-6.10.6.tar.xz
makeコマンド
makeは、LinuxやUnix系システムで使われるビルドツール。makeはMakefileに定義されたルールに基づいて、依存関係の解決やコマンドの実行を行う。カーネルのコンフィギュレーションの設定やコンパイルなどで利用する。
makeコマンドの主なターゲット
ターゲット | 内容 |
clean | 設定ファイル(.config)以外の不要なファイルを削除 |
mrproper | 生成されたすべてのファイル と設定ファイル、各種バックアップファイルを削除 |
distclean | mrproperとエディターのバックアップファイルやパッチファイルを削除 |
defconfig | ARCHが提供する”defconfig”のデフォルトを使用した新しいコンフィグ |
oldconfig | 提供された 設定ファイル(.config)を基に設定を更新 |
savedefconfig | 現在の設定を”./defconfig”として保存 |
tinyconfig | 可能な限り小さなカーネルに設定 |
hardening.config | 基本的なカーネル・ハードニング・オプション |
kvm_guest.config | KVM ゲストとして起動可能 |
nopm.config | 電源管理を無効化 |
rust.config | Rustを有効化 |
all | すべてのターゲットをビルド。vmlinuxコマンドとmodulesコマンドを両方実行したに等しい。 |
vmlinux | ベアカーネルをビルド |
modules | すべてのモジュールをビルド |
modules_install | すべてのモジュールを”INSTALL_MOD_PATH “(デフォルト : /) にインストール |
modules_prepare | 外部モジュールをビルドするための設定を行う |
rpm-pkg | ソースとバイナリの両方のRPMカーネルパッケージをビルド |
srcrpm-pkg | ソースのRPM パッケージのみをビルド |
binrpm-pkg | バイナリカーネルのRPMパッケージのみをビルド |
deb-pkg | ソースとバイナリの両方のdebカーネル・パッケージをビルド |
srcdeb-pkg | ソースのdebパッケージのみをビルド |
bindeb-pkg | バイナリのdebパッケージのみをビルド |
dir-pkg | ディレクトリ構造としてカーネルをビルド |
tar-pkg | 非圧縮tarボールとしてカーネルをビルド |
targz-pkg | gzipで圧縮されたtarボールとしてカーネルをビルド |
tarbz2-pkg | bzip2で圧縮されたtarボールとしてカーネルをビルド |
tarxz-pkg | xzで圧縮されたtarボールとしてカーネルをビルド |
tarzst-pkg | zstd圧縮されたtarボールとしてカーネルをビルド |
makeコマンドのオプション
オプション | 内容 |
-B, –always-make | ターゲットファイルと依存ファイルのタイムスタンプの関係で、ターゲットファイルが最新の状態の場合、そのターゲットファイルを生成するレシピを実行しないが、無条件ですべてのターゲットを作る。 |
-C DIRECTORY, –directory=DIRECTORY | makefileを読み込む前にディレクトリを移動。このオプションは再帰的にmakeを実行するときに便利なオプション。 |
-d | 多くのデバッグ情報を表示 |
–debug[=FLAGS] | 各種デバッグ情報を表示 |
-e, –environment-overrides | makeの変数より環境変数を優先しオーバライド |
-E STRING, –eval=STRING | STRINGをmakefile文として評価 |
-f FILE, –file=FILE, –makefile=FILE | 実行するmakefileを指定 |
-i, –ignore-errors | 実行しているコマンドでエラーが起きた場合、makeの実行が中断される。レシピ内でエラーが起きても、レシピ内のコマンドが続けて実行し続ける。 |
-I DIRECTORY, –include-dir=DIRECTORY | DIRECTORYを検索し、含まれるmakefileを探す |
-j [N], –jobs[=N] | 一度にN個のジョブを許可 |
-k, –keep-going | エラーが起きてもできる限り実行処理を行う。例えば、あるターゲットファイルに対して、複数の依存ファイルが存在するときに、ある依存ファイルのレシピでエラーが起きた場合、そのレシピの処理は中断するが、別の依存ファイルの処理はそのまま行うことができる。 |
-l [N], –load-average[=N], –max-load[=N] | 負荷がNを下回らない限り、複数のジョブを開始しない |
-L, –check-symlink-times | 通常、シンボリックリンクファイルを参照している場合、リンク先のタイムスタンプが参照されるが、このオプションでは、シンボリックリンクファイルのタイムスタンプも参照され、両方のファイルのタイムスタンプから新しい方のタイムスタンプをファイルのタイムスタンプとして扱う。 |
-n, –just-print, –dry-run, –recon | 実際にレシピを実行せずプリントのみ行う |
-o FILE, –old-file=FILE, –assume-old=FILE | 指定したファイルのタイムスタンプが依存ファイルより古い場合等でもmakeのレシピを実行しない |
-O[TYPE], –output-sync[=TYPE] | 並列ジョブの出力をTYPEで同期 |
-p, –print-data-base | Makeの内部データベースをプリント |
-q, –question | レシピを実行せず、終了ステータスは最新かどうかを表示 |
-r, –no-builtin-rules | 組み込みの暗黙のルールを無効化 |
-R, –no-builtin-variables | デフォルトの暗黙的なルールの変数をクリア |
-s, –silent, –quiet | 実行するコマンドを表示しないようにする。コマンドの実行結果が端末に表示されるだけで、実際にどのコマンドが実行されたかは表示されなくなる。 |
–no-silent | “–silent”や”–quiet”オプションで非表示になっている状態を無効化し、コマンドを表示させる。 |
-S, –no-keep-going, –stop | makeのデフォルトの動作を維持する。従って、エラーが発生した時点でビルドを停止する。 |
-t, –touch | レシピを実行する代わりに touch コマンドを実行し、レシピを実行しないで make で生成するファイルのタイムスタンプを変更できる。 |
–trace | トレース情報をプリント |
-w, –print-directory | カレントディレクトリを表示 |
–no-print-directory | 暗黙的にオンになっていたとしても、-w をオフにする。ディレクトリ情報の表示を抑制することができる。 |
-W FILE, –what-if=FILE, –new-file=FILE, –assume-new=FILE | 指定したファイルが変更されたと見なすよう make に指示する。指定したファイルに依存するすべてのターゲットが再ビルドされる。 |
–warn-undefined-variables | 未定義の変数が参照された場合に警告する |
コンフィギュレーションの設定
コンフィギュレーションの設定は、各種機能をカーネルに組み込むか、組み込まないか、または組み込まずにローダブルモジュールとするかを設定する。組み込む場合は[Y]、組み込まない場合は[N]、ローダブルモジュールにする場合は[M]となる。設定した内容は、.confgファイルに記載される。
コンフィギュレーションの設定を変更することでプロセス管理、メモリ管理、ハードウェアとのやり取りなどの機能やデバイスをどのように制御するか設定できる。また、適切な設定を行うことで、システムのパフォーマンスが向上し、不要な機能を排除することでカーネルサイズを小さくすることが可能となる他、セキュリティや特定のデバイスサポートを有効にするためにも重要な役割を果たす。
既存のカーネル設定を使用して新しいカーネルバージョンの設定を行うためのコマンド。カーネルの設定項目は多く、すべての設定項目を一から設定していくのは大変な作業となる。そこで、既存のカーネルのバージョンで使用していた設定を新しいカーネルソースに適用し、変更されたオプションのみユーザーに確認を求めることで、以前の設定を引き継ぎ、新しいオプションや変更点のみ対応する。バージョンアップでなければmake oldconfigを実行する必要はない。
現行のカーネルの設定ファイルは、”/boot”以下のディレクトリに保存されている。自分の開発環境には、”config-6.8.0-40-generic”という設定ファイルが格納してあった。
$ ls /boot/config*
/boot/config-6.8.0-40-generic
この設定ファイルを先ほどカーネルソースをダウンロードして展開したディレクトリにコピーする。
$ cp /boot/config-6.8.0-45-generic ./linux-6.10.6/.config
先程ダウンロードして解凍したカーネルソースのディレクトリに移動し、make oldconfigを実行する。”make oldconfig”を実行すると下記のように、各カーネルの機能について問われるのでy(yes)、n(no)、m(module)、?(help表示)で答えていく。推奨される設定は大文字で表示され、yがY、nがNなどで表示される。[Enter]で推奨される設定になる。
$ cd linux-6.10.6
$ make oldconfig
・
・
・
Synthetic trace events (SYNTH_EVENTS) [Y/?] y
User trace events (USER_EVENTS) [Y/n/?] y
Histogram triggers (HIST_TRIGGERS) [Y/n/?] y
Trace event injection (TRACE_EVENT_INJECT) [Y/n/?] y
Add tracepoint that benchmarks tracepoints (TRACEPOINT_BENCHMARK) [N/y/?] n
Ring buffer benchmark stress tester (RING_BUFFER_BENCHMARK) [N/m/y/?] n
Show eval mappings for trace events (TRACE_EVAL_MAP_FILE) [N/y/?] n
Record functions that recurse in function tracing (FTRACE_RECORD_RECURSION) [N/y/?] n
Validate RCU is on during ftrace execution (FTRACE_VALIDATE_RCU_IS_WATCHING) [N/y/?] (NEW)
Perform a startup test on ftrace (FTRACE_STARTUP_TEST) [N/y/?] n
Verify compile time sorting of ftrace functions (FTRACE_SORT_STARTUP_TEST) [N/y/?] n
Ring buffer startup self test (RING_BUFFER_STARTUP_TEST) [N/y/?] n
Verify ring buffer time stamp deltas (RING_BUFFER_VALIDATE_TIME_DELTAS) [N/y/?] n
Test module for mmiotrace (MMIOTRACE_TEST) [N/m/?] n
Test module to create a preempt / IRQ disable delay thread to test latency tracers (PREEMPTIRQ_DELAY_TEST) [N/m/?] n
Test module for in-kernel synthetic event generation (SYNTH_EVENT_GEN_TEST) [N/m/?] n
Test module for in-kernel kprobe event generation (KPROBE_EVENT_GEN_TEST) [N/m/?] n
Hist trigger debug support (HIST_TRIGGERS_DEBUG) [N/y/?] n
#
# configuration written to .config
#
今回は”make oldconfig”を使ったが、他にもカーネルを設定する方法がある。
コマンド | 内容 |
make config | コンソールでカーネルオプション毎に、答えていき順番に設定していく |
make menuconfig | コンソールでメニュー形式で表示されるオプション項目を選択して設定していく |
make xconfig | X(KDE)で設定する |
make gconfig | X(GNOME)で設定する |
試しに、”make menuconfig”で設定してみる。”libncurses-dev”がインストールされていないため、エラーを吐いている。
$ make menuconfig
*
* Unable to find the ncurses package.
* Install ncurses (ncurses-devel or libncurses-dev
* depending on your distribution).
*
* You may also need to install pkg-config to find the
* ncurses installed in a non-default location.
*
make[2]: *** [scripts/kconfig/Makefile:232: scripts/kconfig/mconf-libs] Error 1
make[1]: *** [/usr/src/linux-6.10.6/Makefile:695: menuconfig] Error 2
make: *** [Makefile:240: __sub-make] Error 2
オプション項目を[Space]を押下し”*”を付けることで設定を有効にできる。
カーネルソースコードをコンパイル
ターゲット指定をせずにmakeを実行すると、カーネルとカーネルモジュールの両方をコンパイルする。指定されたカーネル設定に従って、カーネル本体(vmlinuzなど)、カーネルモジュール(*.koファイル)、オブジェクトファイルなどが生成される。makeコマンドの出力には、カーネルイメージ(arch/x86/boot/bzImageなど)が含まれる。カーネル自体とモジュールがコンパイルされるだけで、この段階ではまだカーネルがインストールされたわけではない。
まず、コンパイルに必要なパッケージをインストールしておく。
$ apt install -y build-essential bc bison flex libelf-dev libssl-dev libncurses5-dev
makeを実行するとエラーが起こることもしばしば。今回は、証明書のファイルが設定されていないことが原因でエラーとなった
$ make
・
・
・
make[3]: *** No rule to make target 'debian/canonical-certs.pem', needed by 'certs/x509_certificate_list'. Stop.
make[2]: *** [scripts/Makefile.build:485: certs] Error 2
make[1]: *** [/usr/src/linux-6.10.6/Makefile:1934: .] Error 2
make: *** [Makefile:240: __sub-make] Error 2
“debian/canonical-certs.pem”がないということ。なので、コンパイルする際の設定ファイル(.config)の証明書のキーの指定先を無くす。まずは、この設定が記述されている箇所を確認する。設定ファイルはかなりの行数で記述されているので”-n”オプションとgrepで設定されている行数を確認する。
$ cat -n .config | grep debian
11833 CONFIG_SYSTEM_TRUSTED_KEYS="debian/canonical-certs.pem"
11841 CONFIG_SYSTEM_REVOCATION_KEYS="debian/canonical-revoked-certs.pem"
viコマンドで設定ファイル(.config)を編集する。”11833G”で11833行目へ移動し、[i]押下で編集モードに切り替え、先ほどの証明書のキーの指定先を””へ変更する。
$ vi .config
・
・
・
#
# Certificates for signature checking
#
CONFIG_MODULE_SIG_KEY="certs/signing_key.pem"
CONFIG_MODULE_SIG_KEY_TYPE_RSA=y
# CONFIG_MODULE_SIG_KEY_TYPE_ECDSA is not set
CONFIG_SYSTEM_TRUSTED_KEYRING=y
CONFIG_SYSTEM_TRUSTED_KEYS="debian/canonical-certs.pem" => ""へ変更
CONFIG_SYSTEM_EXTRA_CERTIFICATE=y
CONFIG_SYSTEM_EXTRA_CERTIFICATE_SIZE=4096
CONFIG_SECONDARY_TRUSTED_KEYRING=y
# CONFIG_SECONDARY_TRUSTED_KEYRING_SIGNED_BY_BUILTIN is not set
CONFIG_SYSTEM_BLACKLIST_KEYRING=y
CONFIG_SYSTEM_BLACKLIST_HASH_LIST=""
CONFIG_SYSTEM_REVOCATION_LIST=y
CONFIG_SYSTEM_REVOCATION_KEYS="debian/canonical-revoked-certs.pem" => ""へ変更
# CONFIG_SYSTEM_BLACKLIST_AUTH_UPDATE is not set
# end of Certificates for signature checking
・
・
・
エラーなくmakeできた。makeは非常に時間を要する。気長に待つしかない。
$ make
・
・
・
カーネルモジュールをインストール
makeが完了したらコンパイルしたカーネルモジュールを適切なディレクトリにインストールする。”make modules_instal”を実行することでカーネルのコンパイルで生成されたすべてのモジュール(*.koファイル)が、指定されたディレクトリ(通常は”/lib/modules/カーネルバージョン”)にインストールされ、カーネルがブートされる際にロード可能なモジュールがシステムに配置される。モジュールの依存関係情報(modules.depファイルなど)もこの段階で生成される。
$ make modules_instal
カーネルイメージをインストール
コンパイルしたカーネルイメージをシステムのブートローダーに適切にインストールする。”make install”を実行することで、コンパイルされたカーネルイメージ(bzImage、vmlinuzなど)及び関連ファイル( System.map、configファイルなど)が、ブートローダーが認識するディレクトリ(通常は/boot/)に新しいカーネルイメージをコピーする。また、初期RAMディスク(initrd又はinitramfs)が必要な場合は、生成して適切な場所に配置される。
$ make install
INSTALL /boot
run-parts: executing /etc/kernel/postinst.d/initramfs-tools 6.10.13 /boot/vmlinuz-6.10.13
update-initramfs: Generating /boot/initrd.img-6.10.13
“make install”`で生成されたファイルは”/boot/”以下に保存される。確認してみると”vmlinuz-バージョン名”で配置されている。
$ ls /boot/
・
・
・
vmlinuz-6.10.13
・
・
・
この時点では、まだデフォルトのカーネルは変更されていない。システムを再起動し、新しいバージョンのカーネルを起動し、問題なく動作することが確認できれば、GRUBの設定でデフォルトのカーネルを新しいバージョンを変更する。
まず、GRUBに登録されているエントリを確認する。
$ grep "menuentry" /boot/grub/grub.cfg | cut -d "'" -f 2
・
・
・
Ubuntu, with Linux 6.10.13
Ubuntu, with Linux 6.10.13 (recovery mode)
Ubuntu, with Linux 6.8.0-45-generic
Ubuntu, with Linux 6.8.0-45-generic (recovery mode)
・
・
・
menuentryで一番最初(0番目)に表示されるのが”Linux 6.10.13″なので、”Linux 6.10.13″のカーネルがデフォルトのカーネルということになる。従って、下記コマンドでデフォルトのカーネルに変更する。
$ grub-set-default 0
設定ファイルを更新する。
$ grub-mkconfig -o /boot/grub/grub.cfg
Sourcing file `/etc/default/grub'
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-6.10.13
Found initrd image: /boot/initrd.img-6.10.13
Found linux image: /boot/vmlinuz-6.10.13.old
Found initrd image: /boot/initrd.img-6.10.13
Found linux image: /boot/vmlinuz-6.8.0-45-generic
Found initrd image: /boot/initrd.img-6.8.0-45-generic
Found memtest86+x64 image: /boot/memtest86+x64.bin
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
システムを再起動後、カーネルのバージョンを確認し、コンパイルしたバージョンと同じであれば問題ない。
$ uname -r
6.10.13
記事を読んでいただきありがとうございました。