286
293

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

LinuxAdvent Calendar 2020

Day 11

Linuxカーネルビルド大全

Last updated at Posted at 2020-12-11

はじめに

(本記事は 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にも使えますが、ブラウザで開くと以下のようにリポジトリの状況を確認することができます。
スクリーンショット 2020-11-29 19-26-26.png

また、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 oldconfigmake 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の実行に使われるので、機能のトグルはスペースキーを使うという事は認知しておくと良いと思います。
menuconfig.png

ソースに付属しているスクリプトで個別に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等で指定したバージョンが見えれば確認完了です。

カーネルモジュールのビルド

さて、これまでカーネル本体のビルド方法を確認してきましたが、実際にはカーネルに手を加えたい場合は、カーネルモジュールとして機能をまとめる方が一般的かと思います。

ここでは例として、モジュールのロード・アンロード時にメッセージを出力する簡単なコードをモジュール化することを考えます。

hello.c
#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と同じディレクトリに置けば準備は完了です。

Makefile
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を書き換えます。

Makefileのdiff
- KDIR := /lib/modules/$(shell uname -r)/build
+ KDIR := /path/to/linux/build

後は同様に makesudo 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の脆弱性の為、機能が制限されてビルドに失敗するため、以下の様に制約をコメントアウトして外します。

/etc/ImageMagick-6/policy.json
<!--以下の行をコメントアウトする-->
<policy domain="coder" rights="none" pattern="PDF" /> 

後は↓を実行すればできるはず。

make pdfdocs

成功するとこんな感じで↓各項目についてのドキュメントが /Documentation/outputs以下に生成されます。
pdfdocs.png
(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メニューが確認できるのであれば、そこから選択すればビルドしたカーネルを使う事ができます。
grub2.png

なお、このメニューを表示させるためには、
grubの設定ファイルを以下のように書き換え、 sudo update-grub(ubuntu)または sudo grub2-mkconfig -o /boot/grub2/grub.cfg(fedora) を実行すればよいはずです。

/etc/default/grub
GRUB_TIMEOUT_STYLE=menu
GRUB_TIMEOUT=5

なお、VMをlibvirt経由でCLIのみで使う場合、以下の様に追記or編集し

etc/default/grub
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-defaultgrub-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

リンク

  1. mrproperは現地の洗剤のキャラクターであるMr.properというおじさんの名前らしい。 linuxビルド界隈?ーーがあるとすればーーでは言わずと知れた存在だと思いますが、こういうのはなんだかなぁという気がする。

  2. このセクションでは、out-of-treeビルドは考慮しない説明をしています。必要がある場合は out-of-treeビルドの章を適宜参照してください。

  3. 僕が試した例だと、fedoraは再起動後上手くカーネルが切り替わりましたが、ubuntuの場合はgrub-reboot等を使っても変わらず、最終的にコンソールからGRUBメニューを表示して手動で選択することで切り替えられました。

  4. vagrantで generic/ubuntu20.04を使って確認した結果です。シリアルデバイスがついていなければつながらないのでその場合は個別に対応が必要です。

286
293
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
286
293

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?