(本記事は Linux Advent Calendar 2020 および 東京大学 品川研究室 Advent Calendar 2020 の11日目の記事として書かれました。)
カーネルのビルドについての情報はググると色々と出てきますが、「ここを見ればOK」と思えるサイトに巡り会えていないので、自分で書いてみることにしたのが本記事です。
いずれLinuxカーネルをビルドする必要にかられるであろう研究室・学科の後輩や、忘れっぽい将来の自分のためにも、改めてLinuxカーネルのビルド方法についてまとめてみたいと思います。
さて、「カーネルをビルドしよう」という状況に置かれた場合、実際にすべきことは主に以下の4点だと思います。
そこで本記事では、カーネル本体のビルド上記の4点の他、関連して以下のトピックについても解説を行おうと思います。
なお、本記事はx86_64を想定しており、他のアーキテクチャでの確認は行っておりません。
使用したディストリビューション及びカーネルのバージョンは以下のとおりです。
それぞれの説明に入る前に、カーネルビルドの具体的なコマンドを手っ取り早く知りたい人(将来の自分)の為に一連の流れを置いておきます。
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でビルドしたカーネルを選択
ソースコードの取得
カーネルのソースは様々な場所で手に入れる事ができますが、基本的には大本のソースを取得するか、各ディストリ向けにパッチの当たったソースを取得して使うかのどちらかだと思います。
アップストリームソースの取得
Ubuntu向けソースの取得
Fedora向けソースの取得
アップストリームソースの取得
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
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
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
を利用することで、現在使われていないモジュールのビルドを無効化することができます。