第524回 ではメインラインビルドのバイナリパッケージのインストール方法を紹介しました。今回はメインラインカーネルをできるだけそのままビルドする方法を紹介します。
Ubuntuのカーネル事情
改めて言うまでもないことではありますが、UbuntuはLinuxディストリビューションの一つです。カーネルにLinuxカーネル を採用し、ツールチェインやユーザーランドにGNU製を始めとするさまざまなFLOSS(Free/Libre Open Source Software)を利用したシステムです。UbuntuのベースとなっているDebianにはカーネルにFreeBSDを採用した「Debian GNU/kFreeBSD 」があるものの、基本的にLinuxディストリビューションと言えばカーネルにLinuxを採用しています[1] 。
さてカーネルはコンピューターシステムにおいてハードウェアとアプリケーションを仲立ちする存在です。つまりあるハードウェアがUbuntuで動くかどうかを調べる場合、カーネルがサポートしているかどうかがひとつの指標となります。もちろん第524回 でも紹介したように、ハードウェアによってはカーネルだけでなく適切なファームウェアが必要だったり、ユーザーランド側も対応が必要だったりすることもあります。逆にカーネル側には汎用のドライバがあれば良く、ユーザーランド側で適切に処理するタイプのデバイスもあります。いずれにせよ、「 カーネルがどうなっているか」をまず確認することになるでしょう。
最新のハードウェアのサポートという観点から考えると、一般的にはより新しいカーネルが好ましいです。しかしながらLinuxディストリビューションとしてのQAを考えると、常に最新版を追いかけるのはなかなか難しいものがあります。そこでUbuntuでは、リリースごとに「サポートするカーネルのバージョン」を固定化し、それに対してUpstreamによるマイナーリリースの取り込み、より新しいカーネルバージョンからの修正パッチのバックポート、Ubuntu独自修正の追加などを行っています。
たとえばここ数回のリリースにおけるUbuntuのバージョンとカーネルのリリース日の比較は次のとおりです。
4.4
2016年1月
16.04 LTSカーネル
4.5
2016年3月
4.6
2016年5月
4.7
2016年7月
4.8
2016年9月
16.10カーネル
4.9
2016年12月
4.10
2017年2月
17.04カーネル
4.11
2017年4月
4.12
2017年6月
17.10カーネル
4.13
2017年9月
4.14
2017年11月
4.15
2018年1月
18.04 LTSカーネル
4.16
2018年4月
4.17
2018年6月
このようにUbuntuの場合は、リリース予定日の2ヶ月ぐらい前までの最新版が目安となります[2] 。
[2] 16.10だけ例外的ではありますが、その理由については不明です。少なくとも4.7がまだリリースされていない5月頭には、4.8を採用することが決まっていたので「LTSではないから直近でもいい」という判断が働いただけかもしれません。
通常のリリースは9ヶ月のサポート期間なので、次のリリースまでの半年でリリース3回分、EOLまで使ったとしてもリリース5回分ぐらいしか離れていません。それに対してLTSは次のリリースまで2年、最大でも5年の利用期間になるため、おおよそリリース12回分から30回分ぐらい同じバージョンのカーネルを使い続けることになります。
そこで導入されているのがHWE(Hardware Enablement) の仕組みです。ざっくり言うと通常リリースで採用したカーネルは9ヶ月メンテナンスするのだから、それを直前のLTSでもインストールできるようにしておこうという仕組みになります。特により新しいハードウェアにUbuntuをインストールするためにはインストーラーの時点で新しいカーネルになっている必要があるため、LTSのポイントリリースのインストーラーにおいては、HWEカーネルが採用されます[3] 。
[3] 言い換えるとLTSで5年メンテナンスされるカーネルを使いたい場合は、LTSの最初のインストーラーを使うか、ポイントリリースのインストーラーでインストールしたあとに、オリジナルのカーネルパッケージをインストールし直す必要があります。もしHWEカーネルを使い続ける場合は、そのHWEカーネルのサポートが切れるまでに、次のHWEカーネルに更新されることになるでしょう。
HWEカーネルについては、若干古い情報ではありますが本連載の第278回 でも紹介していますので、そちらも参照してください。
Ubuntu用のカーネルをビルドする方法
さて、そうは言ってもUbuntuのカーネルバージョンでは都合が悪いことも多々あります。第524回 のような新しいGPUに対応していないのは最たる例でしょう。既存のUbuntuでより新しいカーネルを使う方法はいくつか存在します。
より新しいUbuntuカーネルがリリースされるのをおとなしく待つ
Ubuntuカーネルにパッチを当ててビルドし直す
Ubuntuのメインラインビルドを使う
Upstreamのソースコードをそのままビルドする
カーネルに詳しいと言い切れない人に対しておすすめするのは1.の「おとなしく待つ」選択肢です。UbuntuはあくまでUbuntuの公式リポジトリから提供しているカーネル上での動作を想定しています。独自ビルドを使っていて不具合が発生したときに独自ビルドしたカーネルのせいなのかUbuntuカーネルでも起きるのかを切り分けるところから始めなくてはなりません。また、独自ビルドのカーネルを利用する場合は、自分で設定しない限りセキュアブートは使えません。
ただし本当に単に待つだけでは、自身が抱えている問題が解決されるかどうかは運頼みになります。個々の抱えている問題ごとに、修正されるために必要な情報をUbuntuのカーネルチームやUpstreamに提供したり、メーリングリストやチケットに書き込むなど、解決に向けてのできることはたくさんあるのです。これを機にUbuntu以外のLinuxディストリビューションを使うというのもひとつの手です。
2.は本来のUbuntuカーネルを少し改変することで修正される不具合の場合に有効です。特にUpstreamなどではパッチが存在し取り込まれている場合に使える手でしょう。UbuntuカーネルのソースコードはパッケージリポジトリやカーネルチームのGitリポジトリからダウンロードできます。詳しい手順は第333回 で紹介していますので、そちらを参照してください。
Ubuntuのリリースカーネル以外のバージョンを使いたい場合は、3.がお手軽です。Ubuntuのカーネルチームは個々のカーネルリリースとmasterブランチをそれぞれ毎日ビルドし、そのバイナリパッケージを公開しています。特に遭遇している不具合が、どのバージョンから発生するようになったのか、2分探索(bisect )したい場合に有効です。詳しい使い方は第524回 を参照してください。
最後の4.はUbuntuカーネルではなくUpstreamのカーネルをそのまま使う方法です。ただしカーネルコンフィグはUbuntuのそれを使うことも可能です。たとえば仮想マシンの上など、ユーザーランドがUbuntuではない環境でLinuxオリジナルのカーネル(Vanilla Kernel)をそのまま使いたいケースなどが相当します。ちなみにカーネルそのもの開発に携わっている場合などにも有効ではあると思いますが、そういう人は本記事を読まなくてもわかっていると思いますので対象外とします。
前フリが長くなってしまいましたが、今回はこの4.のケースについて説明します。
カーネルビルド環境の準備
まずはLinuxカーネルのビルドに必要なパッケージをインストールしましょう。
$ sudo apt install git ccache fakeroot libncurses5-dev
$ sudo apt build-dep linux
個別にインストールしているパッケージの用途は次のとおりです。
git:ソースコードの取得・管理に使用する
ccache:ビルドの高速化(今回は使用しません)
fakeroot:カーネルパッケージ作成時に使用
libncurses5-dev:カーネルコンフィグ時のUIをビルドする際に必要
次の「build-dep
」は指定したソースパッケージをビルドする際に必要なパッケージをインストールするサブコマンドです。このコマンドを実行するためには、/etc/apt/sources.list
のdeb-src
フィールドを有効化しておく必要があります。該当するファイルを手で編集するか、デスクトップ環境であれば「ソフトウェアとアップデート」を起動した上で「Ubuntuのソフトウェア」タブの「ソースコード」にチェックを入れてください。
「build-dep
」ではソースパッケージの「Build-Depends
」フィールドに書かれているパッケージ一式をインストールします。
$ apt showsrc linux
(中略)
Build-Depends: debhelper (>= 9), dh-systemd, cpio, kernel-wedge, ...
(後略)
linuxパッケージの場合はおよそ数百MBのパッケージをダウンロード・インストールすることになりますので、ネットワークとストレージがそれなりに潤沢な環境で実行しましょう。また、ビルド時のみ必要なbuild-depパッケージ群をホストシステムにインストールしたくない場合は、第521回 で紹介したLXDなどのように仮想環境上で実行するのも選択肢のひとつになるでしょう。
ちなみに「ビルドする際に必要なパッケージ」に前述のlibncurses5-devなどが入っていないのは、パッケージビルドシステムにおいてはこれらのパッケージが必要ではないためです。あくまでユーザーの環境で、手作業でビルドする際に必要になると思っておけば良いでしょう。
最後にVanillaカーネルの最新のソースコードをダウンロードします。
$ git clone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
$ cd linux
$ mkdir ../build
最後にビルドデータを置くディレクトリを作っています。
カーネルパッケージをビルドする
カーネルパッケージを作成する際は主に、「 カーネルコンフィグを設定する」「 カーネル本体をビルドする」「 カーネルモジュールをビルドする」「 パッケージングする」の順番で行われます。
このうちカーネルコンフィグではカーネルの各種機能のオンオフやデフォルトパラメーターの設定、機能をカーネルに組み込むか外部モジュールにするかの選択などを設定します。ゼロからすべてを設定するにはそれなりの知識が必要ですので、既存の設定をベースに個別に改変していくのが一般的です。Ubuntuの場合は既存のコンフィグファイルを流用しましょう。
Ubuntuが動いている実機がある場合は、/boot
ディレクトリにコンフィグファイルがあります。それをそのままコピーしましょう。
$ cp /boot/config-`uname -r` ../build/.config
実機がない場合や別リリース・別バージョンのカーネルのコンフィグを使いたい場合は、カーネルチームのサイト から持ってくる方法が便利です。特にLXD環境だと/boot
が空なので、ホストからコピーするか以下の方法を使う必要があります。
$ wget -O ../build/.config http://kernel.ubuntu.com/~kernel-ppa/config/bionic/linux/4.15.0-21.22/amd64-config.flavour.generic
フレーバーやアーキテクチャーごとにコンフィグファイルが置いてありますので、用途にあわせて選んでください。
UbuntuカーネルのコンフィグからCONFIG_DEBUG_INFO
を外しておくと、カーネルのビルド時間が短くなります。crashツールなどでデバッグする予定がないのであれば、次のように外しておいてもいいでしょう。
$ scripts/config --file ../build/.config --disable DEBUG_INFO
ダウンロードしたコンフィグを元に最新のカーネルにあわせて作り直します。
$ make O=../build/ olddefconfig
make[1]: ディレクトリ '/home/ubuntu/kernel/build' に入ります
HOSTCC scripts/basic/fixdep
GEN ./Makefile
HOSTCC scripts/kconfig/conf.o
YACC scripts/kconfig/zconf.tab.c
LEX scripts/kconfig/zconf.lex.c
HOSTCC scripts/kconfig/zconf.tab.o
HOSTLD scripts/kconfig/conf
scripts/kconfig/conf --olddefconfig Kconfig
.config:890:warning: symbol value 'm' invalid for HOTPLUG_PCI_SHPC
.config:1144:warning: symbol value 'm' invalid for NF_NAT_REDIRECT
.config:1147:warning: symbol value 'm' invalid for NF_TABLES_INET
.config:1148:warning: symbol value 'm' invalid for NF_TABLES_NETDEV
.config:1331:warning: symbol value 'm' invalid for NF_TABLES_IPV4
.config:1336:warning: symbol value 'm' invalid for NF_TABLES_ARP
.config:1343:warning: symbol value 'm' invalid for NF_NAT_MASQUERADE_IPV4
.config:1378:warning: symbol value 'm' invalid for NF_TABLES_IPV6
.config:1388:warning: symbol value 'm' invalid for NF_NAT_MASQUERADE_IPV6
.config:1416:warning: symbol value 'm' invalid for NF_TABLES_BRIDGE
.config:3992:warning: symbol value 'm' invalid for HW_RANDOM_TPM
.config:4941:warning: symbol value 'm' invalid for LIRC
.config:6167:warning: symbol value 'm' invalid for SND_SOC_INTEL_SST_TOPLEVEL
.config:6172:warning: symbol value 'm' invalid for SND_SOC_INTEL_MACH
.config:7725:warning: symbol value 'm' invalid for DELL_SMBIOS_WMI
.config:7726:warning: symbol value 'm' invalid for DELL_SMBIOS_SMM
#
# configuration written to .config
#
make[1]: ディレクトリ '/home/ubuntu/kernel/build' から出ます
「warning」が出ている部分は適宜確認してください。netfilterの変更 に伴う警告はインパクトが大きそうですが、今回はとりあえず無視します。
カーネルとカーネルモジュールをビルドしましょう。
$ time make -j9 O=../build/ LOCALVERSION=-stock
make[1]: ディレクトリ '/home/ubuntu/kernel/build' に入ります
GEN ./Makefile
scripts/kconfig/conf --syncconfig Kconfig
GEN ./Makefile
WRAP arch/x86/include/generated/uapi/asm/bpf_perf_event.h
HOSTCC scripts/basic/bin2c
(中略)
LD [M] sound/usb/usx2y/snd-usb-usx2y.ko
LD [M] virt/lib/irqbypass.ko
LD [M] sound/x86/snd-hdmi-lpe-audio.ko
make[1]: ディレクトリ '/home/ubuntu/kernel/build' から出ます
real 17m23.013s
user 124m57.950s
sys 11m6.538s
モジュール側は次のようにビルドします。
$ time make modules -j9 O=../build/ LOCALVERSION=-stock
LOCALVERSION
はメインラインビルドとは異なるということを明示するためにつけています。指定してもしなくてもかまいません。またカーネルコンフィグのCONFIG_LOCALVERSION_AUTO
を有効にすると、コミットのハッシュが自動的に入ります。これらのラベルはシグネチャとして使われます。あるカーネルに対してシグネチャの異なるカーネルモジュールをロードできませんので注意してください[4] 。
最後にカーネルパッケージを作成しましょう。4.3以降のLinuxにはオリジナルのMakefileにもDebianパッケージを作成するターゲットが存在するので、それを使用します。
$ make bindeb-pkg O=../build/ LOCALVERSION=-stock
make[1]: ディレクトリ '/home/ubuntu/kernel/build' に入ります
/bin/bash /home/ubuntu/kernel/linux/scripts/package/mkdebian
dpkg-buildpackage -r"fakeroot -u" -a$(cat debian/arch) -b -nc -uc
dpkg-buildpackage: info: source package linux-4.18.0-rc2-stock
dpkg-buildpackage: info: source version 4.18.0-rc2-stock-1
dpkg-buildpackage: info: source distribution bionic
(中略)
INSTALL debian/headertmp/usr/include/xen/ (4 files)
INSTALL debian/headertmp/usr/include/asm/ (62 files)
dpkg-deb: building package 'linux-headers-4.18.0-rc2-stock' in '../linux-headers-4.18.0-rc2-stock_4.18.0-rc2-stock-1_amd64.deb'.
dpkg-deb: building package 'linux-libc-dev' in '../linux-libc-dev_4.18.0-rc2-stock-1_amd64.deb'.
dpkg-deb: building package 'linux-image-4.18.0-rc2-stock' in '../linux-image-4.18.0-rc2-stock_4.18.0-rc2-stock-1_amd64.deb'.
dpkg-genbuildinfo --build=binary
dpkg-genchanges --build=binary >../linux-4.18.0-rc2-stock_4.18.0-rc2-stock-1_amd64.changes
dpkg-genchanges: warning: package linux-image-4.18.0-rc2-stock-dbg in control file but not in files list
dpkg-genchanges: info: binary-only upload (no source code included)
dpkg-source --after-build build
dpkg-buildpackage: info: binary-only upload (no source included)
make[1]: ディレクトリ '/home/ubuntu/kernel/build' から出ます
bindeb-pkg
ターゲットはバイナリパッケージのみを、deb-pkg
ターゲットならソースパッケージも作ってくれます。
$ ls -1 ../*.deb
../linux-headers-4.18.0-rc2-stock_4.18.0-rc2-stock-1_amd64.deb
../linux-image-4.18.0-rc2-stock_4.18.0-rc2-stock-1_amd64.deb
../linux-libc-dev_4.18.0-rc2-stock-1_amd64.deb
Ubuntuのカーネルパッケージと異なり、カーネル本体とモジュール群が同じパッケージにまとめられている点が特徴です。
カーネルパッケージをインストールする
あとはlinux-image*.deb
パッケージを、テストしたいシステム上にインストールするだけです。またlinux-headersは主にDKMSを使う場合などサードパーティのカーネルモジュールをビルドしたい時に、linux-libc-devはカーネルが提供するUAPIのヘッダーファイルを使用するユーザーランドアプリケーションをビルドしたい時にインストールします。
$ sudo apt install ../*.deb
システムを再起動し、GRUBでインストールしたカーネルを選択すれば、新しいカーネルで起動できます。あらかじめ/etc/default/grub
のGRUB_CMDLINE_LINUX_DEFAULT
から「quiet splash
」を削除し、「 sudo update-grub
」コマンドを実行することで起動ログが表示されるようにしておいてもよいでしょう。
これで問題なく起動することを確認したら、コンフィグやコードの修正・ビルド・インストールを繰り返すことで、望みのカーネルの完成です。