はじめに
(本記事は Linux Advent Calendar 2020 および 東京大学 品川研究室 Advent Calendar 2020の11日目の記事として書かれました。)
カーネルのビルドについての情報はググると色々と出てきますが、「ここを見ればOK」と思えるサイトに巡り会えていないので、自分で書いてみることにしたのが本記事です。
いずれLinuxカーネルをビルドする必要にかられるであろう研究室・学科の後輩や、忘れっぽい将来の自分のためにも、改めてLinuxカーネルのビルド方法についてまとめてみたいと思います。
概要
さて、「カーネルをビルドしよう」という状況に置かれた場合、実際にすべきことは主に以下の4点だと思います。
そこで本記事では、カーネル本体のビルド上記の4点の他、関連して以下のトピックについても解説を行おうと思います。
- カーネルモジュールのビルド
- ドキュメントのビルド
- 付属ツールのビルド
- その他雑多トピックいくつか
環境設定
なお、本記事はx86_64を想定しており、他のアーキテクチャでの確認は行っておりません。
使用したディストリビューション及びカーネルのバージョンは以下のとおりです。
- Ubuntu20.04(カーネル: 5.4.0)
- Fedora33(カーネル: 5.9.12)
コマンド例
それぞれの説明に入る前に、カーネルビルドの具体的なコマンドを手っ取り早く知りたい人(将来の自分)の為に一連の流れを置いておきます。
ubuntuでの例
$ git clone https://github.com/torvalds/linux.git # ソースコードを取得
$ cd linux
$ git checkout バージョン # ビルドしたいバージョンにcheckout
$ sudo sed -i 's/# deb-src/deb-src/' /etc/apt/sources.list # build-depのためソースパッケージを有効化
$ sudo apt update # build-depのためソースパッケージを有効化
$ sudo apt build-dep -y linux # カーネルビルドに必要なパッケージをインストール
$ sudo apt install libncurses-dev # make menuconfigに必要な追加パッケージをインストール
$ mkdir ../build # out-of-treeビルド(ソースコードと生成物を分離したビルド)の為のディレクトリを作成
$ make olddefconfig O=../build # 既存のコンフィグをコピー&ビルド用ディレクトリに必要なものを生成
$ cd ../build # 以降、ソースコードの変更を伴わない場合、基本的にはビルド用ディレクトリで作業
$ make localmodconfig # 不要なモジュールを無効化
$ make menuconfig # 個別に機能の選択
$ LOCALVERSION=-mybuild make -j8 # カーネル本体&モジュールのビルド
$ sudo make modules_install # モジュールのインストール
$ sudo make install # カーネル本体のインストール(GRUBのエントリ生成もしてくれる)
$ sudo reboot # 再起動後、必要に応じてgrubでビルドしたカーネルを選択
ソースコードの取得
カーネルのソースは様々な場所で手に入れる事ができますが、基本的には大本のソースを取得するか、各ディストリ向けにパッチの当たったソースを取得して使うかのどちらかだと思います。
アップストリームソースの取得
The Linux Kernel Archivesからtarballを取得する
Linuxカーネルのソースコードを入手する方法として最も簡単なのは、
The Linux Kernel Archives(kernel.org)からtarballをダウンロードするということだと思います。
トップページには主要なバージョンしかありませんが、https://kernel.org/pub/linux/kernel/ を探せば各バージョンのソースが手に入ります。
tarballを取得する方法は、gitで取得する場合と比べて格段に少ない転送量で済むので、手っ取り早くソースコードを取得したい場合は、この方法を利用すると良いでしょう。
gitでリポジトリをcloneする
tarballを取得する方がシンプルですが、開発を行う上ではやはりgitのリポジトリを取得したくなると思います。
Linuxカーネルに関わるgitのリポジトリは
https://git.kernel.org
にまとまっています。
ここにはカーネル本体以外や、開発の為のフォーク等が大量にあり混乱しますが、基本的にはLinux開発者の筆頭であるLinus Torvaldsが管理する以下のリポジトリをcloneすると良いでしょう。
なお、このURLはgit clone
にも使えますが、ブラウザで開くと以下のようにリポジトリの状況を確認することができます。
また、Linusの管理するリポジトリはGithubにもミラーがあるので、Githubでカーネルのコードを管理したい場合は、こちらからフォークするのも良いでしょう。
https://github.com/torvalds/linux
なお蛇足かとは思いますが、cloneしてきたmastarブランチは、直接ビルドに使うには不安定なため、適切なバージョン(tag)にcheckoutしてから使うと良いでしょう。
Ubuntu向けソースの取得
Ubuntuを始めとした各ディストリビューションが使うカーネルは、独自のパッチを当たっている事があります。
そのため、特にメインストリームのカーネルを使う理由がない場合は、ディストリビューションの開発・提供するソースを使うのが良いと思います。
ここでは、 aptでlinuxのソースパッケージを取得する方法とUbuntu開発チームのリポジトリをgitでcloneする方法を紹介します。
aptを用いたUbuntu向けソースの取得
aptでのlinuxソースの取得も実は以下2種類あります。
- linux-source-* パッケージを
sudo apt install
で入れる方法 - linux ソースパッケージを
apt source
で取得する方法
linux-source-* パッケージを sudo apt install
で入れる方法
ここでは5.4.0をインストールすることとします。
全バージョンが提供されているわけではないので、欲しいバージョンが存在するか packages.ubuntu.com で検索して探してみてください。
sudo apt install linux-source-5.4.0
こうすると、Filesystem Hierarchy Standardに従って /usr/src/以下に linux-source-5.4.0.tar.bz2が落とされます。
使う場合は適宜解凍してください。
※ この方法で取得したソースコードに実際にUbuntu独自のパッチが当たっているかは直接確認できてはいません。いつか確認するつもりですが、その辺りの情報をご存知の方がいたらコメント頂けると嬉しいです。
linux ソースパッケージを apt source
で取得する方法
正直理解が怪しいですが、apt系のパッケージ管理システムには、
パッケージのビルドに必要なファイルを取得するための仕組みがあり、
その仕組みを利用してlinuxのソースコードを取得するのがこの方法です。
この仕組みを使うためにはまず、aptのリポジトリを編集して deb-srcから始まるURLを有効化する必要があります。
$ sudo sed -i.bak 's/# deb-src/deb-src/' /etc/apt/sources.list # コメントアウトを外してソースパッケージを有効化
$ sudo apt update
さて、準備ができた所で以下のコマンドを実行すると、カレントディレクトリ にLinuxのソースコードが落とされます。
$ apt source linux # sudoは不要
略
dpkg-source: info: extracting linux in linux-5.4.0
dpkg-source: info: unpacking linux_5.4.0.orig.tar.gz
dpkg-source: info: applying linux_5.4.0-56.62.diff.gz
dpkg-source: info: upstream files that have been modified:
略
今回はUbuntu20.04で試したのですが、以下の4つがカレントディレクトリに落とされていました。
- linux-5.4.0/
- linux_5.4.0-56.62.diff.gz
- linux_5.4.0-56.62.dsc
- linux_5.4.0.orig.tar.gz
特に理由がない場合は下3つのファイルは削除してよいと思います。
gitを用いたUbuntu向けソースの取得
例えば、Ubuntu20.04(focal)向けのUbuntu開発チームのLinuxソースは以下のリポジトリからcloneすることができます。
git://git.launchpad.net/~ubuntu-kernel/ubuntu/+source/linux/+git/focal
なお、僕が試した際は、リクエストが詰まっているのか、コマンドを実行してから実際にcloneが始まるまで数分の待ちが発生しました。
スループット自体は10Mbps程度出ていましたが、Github等からアップストリームのリポジトリをcloneしてから git remote add
で上記リポジトリを追加するなり、git clone --reference --dissociate
を使って転送量を減らすのが無難かと思います。
Fedora向けソースの取得
dnfを用いたFedora向けソースの取得
Ubuntuのaptでのソースの取得と同じ様に、dnfにもrpmパッケージのソースを取得する方法があり、それを使います。
$ dnf download --source kernel
$ ls
kernel-5.9.12-200.fc33.src.rpm
ダウンロードしたrpm(ソース)パッケージはインストールするとHOMEディレクトリにrpmbuildディレクトリを作成します。
linuxのソースやパッチはその中に含まれています。
なお、rpmでのインストール時に各種warningが出るかと思いますが、無視して大丈夫でした。
$ rpm -i kernel-5.9.12-200.fc33.src.rpm
$ cd ~/rpmbuild
$ ls
SOURCES SPECS
$ ls SURCES
略
linux-5.9.tar.xz
略
patch-5.9.12.xz
略
これらのファイルを直接使うこともできますが、
tarball展開及びパッチの適用を自分でするのは少し面倒なので、以下のようにして適用します。
$ rpmbuild -bp ~/rpmbuild/SPECS/kernel.spec
# 出力を見ると展開され、パッチが当たっていることが分かる。
$ ls ~/rpmbuild/BUILD/kernel-5.9.fc33/
linux-5.9.12-200.fc33.x86_64 vanilla-5.9
以上で、ソースの準備は完了です。
ただし、直接ビルドしようとすると、「The source tree is not clean, please run 'make mrproper'」
と怒られたので、 make mrproper
1 を実行してからコンフィグ設定&ビルドをすると良いでしょう。
補足:バージョンを指定する場合は以下のように探してダウンロードできます。
ただ、rpmのリポジトリが有効になっていないせいなのか?よくわかりませんが、
出てくるバージョンは少ないし、centos8で試しても上手く行かなかったので、その点はご了承ください...
$ dnf repoquery kernel
Last metadata expiration check: 0:00:04 ago on Fri 11 Dec 2020 03:12:43 AM UTC.
kernel-0:5.8.15-301.fc33.x86_64
kernel-0:5.9.12-200.fc33.x86_64
$ dnf download --source kernel-5.8.15
gitを用いたFedora向けソースの取得
前述のdnfでのソースの取得の際に用いた kernelパッケージの公式ページらしきサイト
を見てみると、
The kernel is maintained in a source tree rather than directly in dist-git.
というわけで、GitLabのリンクを示されます。
各ブランチやtagの細かい意味まではわかりませんが、ここをcloneすれば良いでしょう。
ビルド環境の構築
さて、ソースコードを手に入れたら次はカーネル開発に必要なパッケージをインストールする必要があります。
Ubuntuでのカーネルビルド環境の構築
Ubuntuでは、各パッケージの開発に必要なパッケージをインストールするために、apt build-dep
というコマンドを使うことができます。
ただし、より新しいバージョンのカーネルをビルドする際やmakeのターゲットに依っては追加のパッケージが必要になるので、適宜インストールしてください。
sudo sed -i.bak 's/# deb-src/deb-src/' /etc/apt/sources.list # ソースパッケージの有効化
sudo apt update
sudo apt build-dep -y linux # linuxの開発に必要なパッケージをインストール。
sudo apt install -y fakeroot # make bindeb-pkg等に必要
sudo apt install -y libncurses-dev # make menuconfigに必要
実際に、必要なパッケージが足りていない場合は、makeの出力を見れば分かるようになっています。
例えば、 apt build-dep linux
をしただけで make menuconfig
を実行するとncursesのパッケージが足りないので以下のような表示が出ます。
$ make menuconfig
略
* Unable to find the ncurses package.
* Install ncurses (ncurses-devel or libncurses-dev
* depending on your distribution).
略
*-devel
はrpmパッケージ、*-dev
はdebパッケージなので
Ubuntuの場合はlibncurses-dev
をインストールします
Fedoraでのカーネルビルド環境の構築
Ubuntuでは apt build-dep linux
を使ったのと同様に、
以下のコマンドを実行すればdnfでのビルド環境は構築できます。
sudo dnf builddep -y kernel
僕が試した限りでは、これだけでもビルドは可能でしたが、
kernel-devel
パッケージも提供されており、それをインストールする旨を書いている記事も見るので、
インストールしておくと無難かなと思います。
ひょっとすると、builddepで入るパッケージの中に既にあるかも知れませんが...
カーネルコンフィグの準備
※注意 2
カーネルのビルドをする際は、ビルドを行うディレクトリに設定ファイル(.config
)を置く必要があります。
また、このファイルを書き換えることで、ビルドするカーネルの各種機能の有効化/無効化を行います。
現在のconfigをコピーして使う
カーネルコンフィグには、基本的には現在利用しているカーネルをベースとすると簡単です。
/boot/config-[カーネルバージョン]
にあるので、
ビルドを行うマシンとインストールするマシンが同じ場合は、以下のようにしてコピーします
cp /boot/config-$(uname -r) /path/to/build/dir
また、 ビルドを行うディレクトリにコンフィグファイルが存在しない場合、
make oldconfig
や make olddefconfig
等を実行することでも現在利用中のコンフィグをコピーするとができます。
config変更用のMakeターゲットを使う
異なるバージョンのカーネルをビルドする場合や、特定の機能を有効化/無効化したい場合は
現在利用しているカーネルコンフィグのコピーをそのまま使うことができません。
.config
をエディタで開いて編集することも可能ではありますが、やや面倒です。
そこで、カーネルのMakefileが用意している各種ターゲットを使うと便利です。
make help
とすればターゲットの一覧と説明を見ることができます。
個人的には以下の順で実行する場合が多いので、それぞれ説明します。
$ make olddefconfig
$ make localmodconfig
$ make menuconfig
make oldconfig と make olddefconfig
このMakeターゲットは、既存のコンフィグ(なければ/boot/以下にある現在のカーネルコンフィグ)を元に新しいコンフィグを作る際に使います。
既存のコンフィグから判断できないものはCLIで 以下のように選択します。
AMD 8111 GPIO driver (GPIO_AMD8111) [N/m/y/?] n
BT8XX GPIO abuser (GPIO_BT8XX) [N/m/y/?] (NEW) n
OKI SEMICONDUCTOR ML7213 IOH GPIO support (GPIO_ML_IOH) [N/m/y/?] n
ACCES PCI-IDIO-16 GPIO support (GPIO_PCI_IDIO_16) [N/m/y/?] n
ACCES PCIe-IDIO-24 GPIO support (GPIO_PCIE_IDIO_24) [N/m/y/?] n
make oldconfig
のほうが一般的かと思いますが、 make olddefconfig
は可能な限りデフォルト値に設定してくれて楽なので、僕は重宝しています。
make localmodconfig
カーネルのビルドを行う際は、同時にカーネルモジュールをビルドすることが基本ですが、
実際には利用しないモジュールのビルドに多くの時間がかかってしまうことも多いです。
そこで、make localmodconfig
を利用することで、現在使われていないモジュールのビルドを無効化することができます。
make menuconfig
make menuconfig
を使うと、以下のようにグラフィカルに各機能の設定をすることができます。
基本的には見た通り使えると思いますが、Enterキーは下部の Select, Exit, Help, Save, Loadの実行に使われるので、機能のトグルはスペースキーを使うという事は認知しておくと良いと思います。
ソースに付属しているスクリプトで個別にconfigを設定する
Linuxのソースツリーの scripts
ディレクトリ配下には、様々な有用なスクリプトがあるのですが、
scripts/config
は変更したいカーネルコンフィグ名が既知の場合に有用です。
今回は例として DEBUG_INFO
を変更してみます。
$ grep 'DEBUG_INFO' .config # 現状確認
CONFIG_DEBUG_INFO=y
# CONFIG_DEBUG_INFO_REDUCED is not set
# CONFIG_DEBUG_INFO_SPLIT is not set
CONFIG_DEBUG_INFO_DWARF4=y
# CONFIG_DEBUG_INFO_BTF is not set
$ ./scripts/config --disable DEBUG_INFO # 無効化
$ grep 'DEBUG_INFO' .config # 無効化を確認
# CONFIG_DEBUG_INFO is not set
# CONFIG_DEBUG_INFO_REDUCED is not set
# CONFIG_DEBUG_INFO_SPLIT is not set
CONFIG_DEBUG_INFO_DWARF4=y
# CONFIG_DEBUG_INFO_BTF is not set
$ ./scripts/config --enable DEBUG_INFO # 有効化
$ grep 'DEBUG_INFO' .config # 有効化を確認
CONFIG_DEBUG_INFO=y
# CONFIG_DEBUG_INFO_REDUCED is not set
# CONFIG_DEBUG_INFO_SPLIT is not set
CONFIG_DEBUG_INFO_DWARF4=y
# CONFIG_DEBUG_INFO_BTF is not set
なお、out-of-treeビルドの際は、--file
オプションでビルド用ディレクトリにある .config
を直接指定するか、
ビルド用ディレクトリにスクリプト自体をコピーする必要があるので注意してください。
ビルド及びインストール
カーネルのビルド&インストール方法は複数ありますが、ここでは以下3つの方法を紹介します。
- ビルド、インストール共にmakeで行う方法
- debパッケージをビルドして、aptやdpkgでインストールする方法
- targzパッケージをビルドしてインストールする方法
ビルド、インストール共にmakeで行う方法
$ # コンフィグの生成等は省略
$ LOCALVERSION=-dev make -j8
$ sudo make modules_install
$ sudo make install
基本的にはこれで十分です。
ただ、GRUB(ブートローダー)の設定に依っては再起動しても利用されるカーネルが変わらないという事もあるので3、そういう場合はGRUBについて確認してみましょう。
debパッケージをビルドして、aptやdpkgでインストールする方法
Linuxでは、標準でdebパッケージをビルドするためのターゲットがついています。
targz形式のパッケージをビルドして、インストールする方法
targz形式のパッケージをビルドすると、中には以下のファイルが含まれています。
- /boot/System.map-*
- /boot/config-*
- /boot/vmlinuz-*
- /boot/vmlinux-*
- /lib/modules/*/ ディレクトリ(モジュール等から構成される)
これらをインストールしたい場合、/bootや/lib/modules以下に配置し、ブートローダーの設定をアップデートを行います。
ファイルは単純にコピーすればよく、GRUBについてはupdate-grub
(ubuntu)やgrubby
等GRUB関連のコマンド実行することになります。
アドベントカレンダーということで、締切が来てしまったので、GRUB関連のコマンドについての詳細は後日追記します...
インストールの確認
再起動後に、実際にビルドしたカーネルが使われているかどうかは、uname -r
を実行することで確認します。 LOCALVERSION
等で指定したバージョンが見えれば確認完了です。
カーネルモジュールのビルド
さて、これまでカーネル本体のビルド方法を確認してきましたが、実際にはカーネルに手を加えたい場合は、カーネルモジュールとして機能をまとめる方が一般的かと思います。
ここでは例として、モジュールのロード・アンロード時にメッセージを出力する簡単なコードをモジュール化することを考えます。
#include <linux/module.h>
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("author <author@example.com>");
MODULE_DESCRIPTION("Hello module");
static int __init hello_module_init(void) {
printk(KERN_INFO "Hello module is loaded\n");
return 0;
}
static void __exit hello_module_exit(void) {
printk(KERN_INFO "Hello module is removed\n");
}
module_init(hello_module_init);
module_exit(hello_module_exit);
カーネルモジュール単体でのビルド
カーネルソース自体がない場合でも
/lib/modules/$(shell uname -r)/build
にあるMakefileやリソースを活用することでカーネルモジュールをビルドできます。
以下のMakefileを先程のhello.cと同じディレクトリに置けば準備は完了です。
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
obj-m := hello.o
.PHONY: all clean install
all:
make -C $(KDIR) M=$(PWD) modules
clean:
make -C $(KDIR) M=$(PWD) clean
install:
make -C $(KDIR) M=$(PWD) modules_install
それでは実際にビルドしてみましょう
$ mkdir hello_module
$ cd hello_module
$ vim hello.c # 上記ソースコードを生成
$ vim Makefile # 上記Makefileを生成
$ make
$ sudo make install
これでインストールできると思います。
ビルド済みのカーネルモジュールは /lib/modules/$(uname -r)/extra/
にインストールされているはずなので、
以下のようにしてモジュールの確認ができるはずです。
$ sudo insmod /lib/modules/$(uname -r)/extra/hello.ko
$ sudo rmmod hello
$ sudo dmesg
略
[ 33.438701] RPC: Registered tcp NFSv4.1 backchannel transport module.
[ 33.464984] FS-Cache: Netfs 'nfs' registered for caching
[ 815.217285] hello: loading out-of-tree module taints kernel.
[ 815.217563] hello: module verification failed: signature and/or required key missing - tainting kernel
[ 815.219284] Hello module is loaded
[ 830.556290] Hello module is removed
カーネル本体と合わせたモジュールビルド
自分でカーネル本体もビルドし、そのカーネルと共に使うカーネルモジュールをビルドしたい場合基本的には同じ流れとなります。
まずはカーネル本体をビルドし、その際に利用したディレクトリ(out-of-treeビルドなら O=
で指定したディレクトリ、そうでなければソースのディレクトリ)のパスに前に示したモジュールビルド用のMakefileのKDIRを書き換えます。
- KDIR := /lib/modules/$(shell uname -r)/build
+ KDIR := /path/to/linux/build
後は同様に make
、 sudo make install
すれば新しいカーネル向けのモジュールのビルド&インストールが完了します。
ドキュメントのビルド
Linuxのソースツリーの Documentation
ディレクトリ配下には、多数ドキュメントが存在しており、
それらはmakeを使うことでPDFやHTML等としてビルドすることが可能です。
なお生のドキュメントは https://www.kernel.org/doc/Documentation/ に、
HTMLとして生成されたドキュメントは https://www.kernel.org/doc/html/latest/ でそれぞれ見ることができます。
さて、具体的なビルドの手順についてですが、どうしても大量のwarningやerrorが消せなかったので、
Ubuntu20.04✕linux 5.4.0ソースでpdfdocsの生成を試した手順のみ
pdfdocsビルド手順
まず、以下のように追加で必要のパッケージをインストールします。
$ sudo apt install imagemagick graphviz
ただ、imagemagickの脆弱性の為、機能が制限されてビルドに失敗するため、以下の様に制約をコメントアウトして外します。
<!--以下の行をコメントアウトする-->
<policy domain="coder" rights="none" pattern="PDF" />
後は↓を実行すればできるはず。
make pdfdocs
成功するとこんな感じで↓各項目についてのドキュメントが /Documentation/outputs
以下に生成されます。
(targzパッケージを生成後だったので、LOCALVERSIONがそのままになってしまっている...)
付属ツールのビルド
Linuxカーネルには、カーネルやモジュール、ドキュメントの他に、perf等ユーザーが使えるツールも付属しており、tools
ディレクトリ配下に置いてあります。
tools ディレクトリでmake all
を実行すると全ツールがビルドされ、make perf
の様に、ツール名をしてして個別にビルドすることも可能な他、make help
を実行すれば説明が見れます。
ただ、toolsのビルドは他のビルドと異なり、O=
によるout-of-treeビルドはサポートしていないので注意してください。
落ち穂拾い
out-of-treeビルド
linuxカーネルをビルドする際、特に何も考えずソースツリーの中でmakeを実行すると、.configや.o等がソースツリーの中に生成されて散らかってしまいます。
そこでビルドの生成物を出力するためのデレクトリを別に作成してビルドすることで、ソースツリーが汚れることを防ぎます。
具体的には、makeの実行時に O=
で生成物出力用のディレクトリを指定してやることで実現できます。
$ cd linux
$ mkdir ../build
$ make oldconfig O=../build
$ make menuconfig O=../build
$ make -j8 O=../build
$ sudo make modules_install O=../build
$ sudo make install O=../build
ただ、この方法では、毎回 O=
を指定する必要があり少し面倒です。
別の方法として、 O=
の代わりに環境変数KBUILD_OUTPUT
にビルド用ディレクトリのパスを設定することも可能です。
しかしこの方法も、 sudo
時に環境変数が引き継がれるかどうかを気にする必要があります。
ではどうすると良いかと言うと、実は1度 O=
を付けてビルドすると、その先のディレクトリにもMakefile等が生成されるという機能を活用します。
1度O=
付きでmakeを実行し、以後そのディレクトリでmakeを叩けばソースディレクトリを汚さすに済みます。
$ cd ./linux
$ mkdir ../build
$ make oldconfig O=../build
$ cd ../build
$ make menuconfig
$ make -j8
$ sudo make modules_install
$ sudo make install O=../build
とはいえ、ついうっかりソースツリーでmakeを実行してしまい、汚してしまう可能性もあります。
そういうミスを防ぐために、僕はソースツリーを 読み込み専用のbind mountで保護しています。
$ sudo mount --bind -r ./linux ./linux
dockerを使ってビルドする際は -v
オプションの末尾に :ro
を付けることで読み込み専用のbind mountを利用できます。
$ docker run -v $PWD/linux:/usr/src/linux:ro .....
GRUBの設定
sudo make install
やdebパッケージをインストールするだけでは、次回起動時に自動で新しいカーネルが選択されない場合もあります。
その時はGRUBの設定を確認しましょう。
GRUBメニューでの選択
↓こんな感じでGRUBメニューが確認できるのであれば、そこから選択すればビルドしたカーネルを使う事ができます。
なお、このメニューを表示させるためには、
grubの設定ファイルを以下のように書き換え、 sudo update-grub
(ubuntu)または sudo grub2-mkconfig -o /boot/grub2/grub.cfg
(fedora) を実行すればよいはずです。
GRUB_TIMEOUT_STYLE=menu
GRUB_TIMEOUT=5
なお、VMをlibvirt経由でCLIのみで使う場合、以下の様に追記or編集し
GRUB_CMDLINE_LINUX="console=ttyS0,115200n8"
GRUB_SERIAL_COMMAND="serial --speed=115200 --unit=0 --word=8 --parity=no --stop=1"
GRUB_TERMINAL="serial"
(GRUB_CMDLINE_LINUXは、元が空でなければ、空白区切りでつなげると良いと思います。)
以下のように実行すればGRUBメニューが見えるはずです4。
virsh start --console ドメイン名
grub-set-default/grub-rebootでの選択
vagrant等CLI環境のみで仮想マシンで開発している場合、そもそもGRUBメニューが見れるようにするのもやや面倒です。
そういう場合はgrub-set-default
や grub-reboot
を使うと起動されるカーネルを変更することができます。
$ sudo grub-reboot 指定したいカーネル
または
$ sudo grub-set-default 指定したいカーネル
ちなみに grub-reboot
は次回起動するカーネルを、 grub-set-default
は以降デフォルトで起動するカーネルを指定します。
さて、ここで「指定したいカーネル」というのは、GRUBメニューに表示されるものと同じであり、
以下の様にして選択可能なカーネル一覧を確認できます。
$ egrep '^\s*(menuentry|submenu)\s' /boot/grub/grub.cfg
menuentry 'Ubuntu' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-simple-e75828cc-39b8-4553-a039-49d967b96126' {
submenu 'Advanced options for Ubuntu' $menuentry_id_option 'gnulinux-advanced-e75828cc-39b8-4553-a039-49d967b96126' {
menuentry 'Ubuntu, with Linux 5.4.0-dev' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-5.4.0-dev-advanced-e75828cc-39b8-4553-a039-49d967b96126' {
menuentry 'Ubuntu, with Linux 5.4.0-dev (recovery mode)' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-5.4.0-dev-recovery-e75828cc-39b8-4553-a039-49d967b96126' {
menuentry 'Ubuntu, with Linux 5.4.0-33-generic' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-5.4.0-33-generic-advanced-e75828cc-39b8-4553-a039-49d967b96126' {
menuentry 'Ubuntu, with Linux 5.4.0-33-generic (recovery mode)' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-5.4.0-33-generic-recovery-e75828cc-39b8-4553-a039-49d967b96126' {
menuentry 'Ubuntu, with Linux 5.4.0-26-generic' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-5.4.0-26-generic-advanced-e75828cc-39b8-4553-a039-49d967b96126' {
menuentry 'Ubuntu, with Linux 5.4.0-26-generic (recovery mode)' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-5.4.0-26-generic-recovery-e75828cc-39b8-4553-a039-49d967b96126' {
例えば 5.4.0-dev を起動したい場合は
$ grub-reboot "Advanced options for Ubuntu' $menuentry_id_option >Ubuntu, with Linux 5.4.0-dev" # 入れ子のキーを'>'でつなぐ
または
$ grub-reboot '1>0' # 0-indexの配列に見立てても指定できる
の様に指定します。
補足
理由は判明していませんが、自分で試した範囲では、grub-rebootやgrub-set-defaultが効かず、最悪起動しても接続できない場合がありました。
その場合、GRUBメニューを出せるようにすれば上手くいきました。(GRUBメニューを見ずにタイムアウトでデフォルトを選択させることができるので、それを上手く活用すればよいでしょう。)
llvmでのビルド
歴史的に、linuxカーネルはgccを用いてビルドされて来ました。
しかし近年ではllvmによるビルドもサポートされています。
コンパイラのみgccでなくclangを用いたい場合は、以下のように CC
を書き換えます。
$ make CC=clang defconfig
$ make CC=clang
GNU binutilsのツールチェーン一式LLVMに置き換えたい場合は以下のようにします。
$ make LLVM=1
より詳しくは、カーネルのドキュメントをご覧ください。
Building Linux with Clang/LLVM
クロスコンパイル
TODO
セキュアブートと署名
TODO
リンク
- The Linux Kernel Archives
- https://github.com/torvalds/linux.git
- https://www.kernel.org/doc/makehelp.txt
- Linux Kernel Document: Kernel Build System
- Archi Wiki カーネル/コンパイル/伝統的な方法
- Archi Wiki カーネルモジュールのコンパイル
- Ubuntu wiki Kernel
- Ubuntu Weekly Recipe 第524回 Hades Canyon/Kaby Lake GのdGPUを有効化する -- 新しい開発中のカーネルをビルド&インストールする
- Ubuntu Weekly Recipe 第526回 Ubuntuで最新のカーネルをお手軽にビルドする方法
- Ubuntu Weekly Recipe 第384回 Initramfsのしくみ
- Ubuntu Weekly Recipe 第333回 カーネルパッケージをビルドしよう
- Ubuntu Weekly Recipe 第278回 Ubuntuカーネルとの付き合い方
- Fedora 31でカーネルソースコードを用意する
-
mrproperは現地の洗剤のキャラクターであるMr.properというおじさんの名前らしい。 linuxビルド界隈?ーーがあるとすればーーでは言わずと知れた存在だと思いますが、こういうのはなんだかなぁという気がする。 ↩
-
このセクションでは、out-of-treeビルドは考慮しない説明をしています。必要がある場合は out-of-treeビルドの章を適宜参照してください。 ↩
-
僕が試した例だと、fedoraは再起動後上手くカーネルが切り替わりましたが、ubuntuの場合はgrub-reboot等を使っても変わらず、最終的にコンソールからGRUBメニューを表示して手動で選択することで切り替えられました。 ↩
-
vagrantで generic/ubuntu20.04を使って確認した結果です。シリアルデバイスがついていなければつながらないのでその場合は個別に対応が必要です。 ↩