Ubuntu Weekly Recipe

第526回Ubuntuで最新のカーネルをお手軽にビルドする方法

第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のバージョンとカーネルのリリース日の比較は次のとおりです。

バージョン リリース日 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]⁠。

通常のリリースは9ヶ月のサポート期間なので、次のリリースまでの半年でリリース3回分、EOLまで使ったとしてもリリース5回分ぐらいしか離れていません。それに対してLTSは次のリリースまで2年、最大でも5年の利用期間になるため、おおよそリリース12回分から30回分ぐらい同じバージョンのカーネルを使い続けることになります。

そこで導入されているのがHWE(Hardware Enablement)の仕組みです。ざっくり言うと通常リリースで採用したカーネルは9ヶ月メンテナンスするのだから、それを直前のLTSでもインストールできるようにしておこうという仕組みになります。特により新しいハードウェアにUbuntuをインストールするためにはインストーラーの時点で新しいカーネルになっている必要があるため、LTSのポイントリリースのインストーラーにおいては、HWEカーネルが採用されます[3]⁠。

HWEカーネルについては、若干古い情報ではありますが本連載の第278回でも紹介していますので、そちらも参照してください。

Ubuntu用のカーネルをビルドする方法

さて、そうは言ってもUbuntuのカーネルバージョンでは都合が悪いことも多々あります。第524回のような新しいGPUに対応していないのは最たる例でしょう。既存のUbuntuでより新しいカーネルを使う方法はいくつか存在します。

  1. より新しいUbuntuカーネルがリリースされるのをおとなしく待つ
  2. Ubuntuカーネルにパッチを当ててビルドし直す
  3. Ubuntuのメインラインビルドを使う
  4. 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.listdeb-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/grubGRUB_CMDLINE_LINUX_DEFAULTからquiet splashを削除し、sudo update-grubコマンドを実行することで起動ログが表示されるようにしておいてもよいでしょう。

これで問題なく起動することを確認したら、コンフィグやコードの修正・ビルド・インストールを繰り返すことで、望みのカーネルの完成です。

おすすめ記事

記事・ニュース一覧